package migration

import (
	"context"
	"errors"
	"fmt"

	"github.com/jackc/pgx/v5"

	"github.com/ultisuite/ulti-backend/internal/api/query"
)

const (
	ItemStatusImported = "imported"
	ItemStatusFailed   = "failed"
	ItemStatusSkipped  = "skipped"
)

// JobAuditItem is one row in a migration job item audit report.
type JobAuditItem struct {
	SourceID   string `json:"source_id"`
	RelPath    string `json:"rel_path,omitempty"`
	Status     string `json:"status"`
	Reason     string `json:"reason,omitempty"`
	ImportedAt string `json:"imported_at"`
}

// JobAuditSummary counts items by status for a migration job.
type JobAuditSummary struct {
	Service  string         `json:"service"`
	Imported int64          `json:"imported"`
	Failed   int64          `json:"failed"`
	Skipped  int64          `json:"skipped"`
	Total    int64          `json:"total"`
	ByStatus map[string]int64 `json:"by_status,omitempty"`
}

func (s *Service) verifyJobInProject(ctx context.Context, projectID, jobID string) (service string, err error) {
	err = s.db.QueryRow(ctx, `
		SELECT service FROM migration_jobs
		WHERE id = $1::uuid AND project_id = $2::uuid
	`, jobID, projectID).Scan(&service)
	if errors.Is(err, pgx.ErrNoRows) {
		return "", fmt.Errorf("job not found")
	}
	return service, err
}

func (s *Service) JobAuditSummary(ctx context.Context, projectID, jobID string) (JobAuditSummary, error) {
	service, err := s.verifyJobInProject(ctx, projectID, jobID)
	if err != nil {
		return JobAuditSummary{}, err
	}

	rows, err := s.db.Query(ctx, `
		SELECT status, COUNT(*)
		FROM migration_imported_items
		WHERE job_id = $1::uuid
		GROUP BY status
	`, jobID)
	if err != nil {
		return JobAuditSummary{}, err
	}
	defer rows.Close()

	summary := JobAuditSummary{Service: service, ByStatus: map[string]int64{}}
	for rows.Next() {
		var status string
		var count int64
		if err := rows.Scan(&status, &count); err != nil {
			return JobAuditSummary{}, err
		}
		summary.ByStatus[status] = count
		summary.Total += count
		switch status {
		case ItemStatusImported:
			summary.Imported = count
		case ItemStatusFailed:
			summary.Failed = count
		case ItemStatusSkipped:
			summary.Skipped = count
		}
	}
	return summary, rows.Err()
}

func (s *Service) ListJobAudit(
	ctx context.Context,
	projectID, jobID, statusFilter string,
	params query.ListParams,
) ([]JobAuditItem, query.PaginationMeta, error) {
	if _, err := s.verifyJobInProject(ctx, projectID, jobID); err != nil {
		return nil, query.PaginationMeta{}, err
	}

	statusFilter = normalizeAuditStatusFilter(statusFilter)

	var total int64
	countSQL := `SELECT COUNT(*) FROM migration_imported_items WHERE job_id = $1::uuid`
	countArgs := []any{jobID}
	if statusFilter != "" {
		countSQL += ` AND status = $2`
		countArgs = append(countArgs, statusFilter)
	}
	if err := s.db.QueryRow(ctx, countSQL, countArgs...).Scan(&total); err != nil {
		return nil, query.PaginationMeta{}, err
	}

	listSQL := `
		SELECT source_id, rel_path, status, reason, imported_at::text
		FROM migration_imported_items
		WHERE job_id = $1::uuid
	`
	listArgs := []any{jobID}
	if statusFilter != "" {
		listSQL += ` AND status = $2`
		listArgs = append(listArgs, statusFilter)
	}
	listSQL += ` ORDER BY imported_at DESC, source_id ASC LIMIT $` + fmt.Sprint(len(listArgs)+1) +
		` OFFSET $` + fmt.Sprint(len(listArgs)+2)
	listArgs = append(listArgs, params.Limit(), params.Offset())

	rows, err := s.db.Query(ctx, listSQL, listArgs...)
	if err != nil {
		return nil, query.PaginationMeta{}, err
	}
	defer rows.Close()

	out := make([]JobAuditItem, 0, params.Limit())
	for rows.Next() {
		var item JobAuditItem
		if err := rows.Scan(&item.SourceID, &item.RelPath, &item.Status, &item.Reason, &item.ImportedAt); err != nil {
			return nil, query.PaginationMeta{}, err
		}
		out = append(out, item)
	}
	if err := rows.Err(); err != nil {
		return nil, query.PaginationMeta{}, err
	}
	return out, params.Meta(&total), nil
}

func normalizeAuditStatusFilter(raw string) string {
	switch raw {
	case ItemStatusImported, ItemStatusFailed, ItemStatusSkipped:
		return raw
	default:
		return ""
	}
}

func incJobStat(stats map[string]any, key string) {
	if stats == nil {
		return
	}
	v, _ := stats[key].(float64)
	stats[key] = v + 1
}
