package job

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"strings"

	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/repository"
)

const userExportJobType = "user-export"

type JobService struct {
	store *repository.Store
	cfg   config.Config
}

func NewService(store *repository.Store, cfg config.Config) *JobService {
	return &JobService{store: store, cfg: cfg}
}

func (s *JobService) ListJobs(options repository.ListOptions) ([]model.AdminJob, int64, error) {
	return s.store.ListJobs(options)
}

func (s *JobService) GetJob(id uint) (*model.AdminJob, error) {
	return s.store.FindJobByID(id)
}

func (s *JobService) CreateUserExportJob(actor shared.Actor, options repository.ListOptions) (*model.AdminJob, error) {
	payload, err := json.Marshal(options)
	if err != nil {
		return nil, err
	}
	job := model.AdminJob{
		Type:          userExportJobType,
		Status:        model.JobStatusPending,
		PayloadJSON:   string(payload),
		MaxAttempts:   s.cfg.Jobs.MaxAttempts,
		CreatedBy:     actor.UserID,
		CreatedByName: actor.Username,
	}
	if job.MaxAttempts <= 0 {
		job.MaxAttempts = 3
	}
	if err := s.store.CreateJob(&job); err != nil {
		return nil, err
	}
	return &job, nil
}

func (s *JobService) CancelJob(id uint) error {
	return s.store.CancelJob(id)
}

func (s *JobService) JobArtifact(id uint) (*model.AdminJob, string, error) {
	job, err := s.store.FindJobByID(id)
	if err != nil {
		return nil, "", err
	}
	if job.Status != model.JobStatusSucceeded || strings.TrimSpace(job.ArtifactPath) == "" {
		return nil, "", errors.New("job artifact is not ready")
	}
	artifactPath, err := safeArtifactPath(s.cfg.Jobs.ArtifactDir, job.ArtifactPath)
	if err != nil {
		return nil, "", err
	}
	if _, err := os.Stat(artifactPath); err != nil {
		return nil, "", err
	}
	return job, artifactPath, nil
}

func safeArtifactPath(root string, artifactPath string) (string, error) {
	root = strings.TrimSpace(root)
	if root == "" {
		root = "storage/exports"
	}
	cleanRoot, err := filepath.Abs(root)
	if err != nil {
		return "", err
	}
	cleanArtifact := filepath.Clean(strings.TrimSpace(artifactPath))
	if filepath.IsAbs(cleanArtifact) {
		cleanArtifact, err = filepath.Abs(cleanArtifact)
		if err != nil {
			return "", err
		}
		if cleanArtifact == cleanRoot || strings.HasPrefix(cleanArtifact, cleanRoot+string(os.PathSeparator)) {
			return cleanArtifact, nil
		}
		return "", errors.New("invalid artifact path")
	}
	fullPath, err := filepath.Abs(filepath.Join(cleanRoot, cleanArtifact))
	if err != nil {
		return "", err
	}
	if fullPath != cleanRoot && !strings.HasPrefix(fullPath, cleanRoot+string(os.PathSeparator)) {
		return "", errors.New("invalid artifact path")
	}
	return fullPath, nil
}
