package usecase

import (
	"context"
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/gofrs/uuid/v5"
	"github.com/saveinmed/backend-go/internal/domain"
)

type ImportReport struct {
	TotalProcessed int      `json:"total_processed"`
	SuccessCount   int      `json:"success_count"`
	FailedCount    int      `json:"failed_count"`
	Errors         []string `json:"errors"`
}

// ImportProducts parses a CSV file and batch inserts valid products.
// CSV Headers expected: name,ean,price,stock,description
func (s *Service) ImportProducts(ctx context.Context, sellerID uuid.UUID, r io.Reader) (*ImportReport, error) {
	reader := csv.NewReader(r)
	rows, err := reader.ReadAll()
	if err != nil {
		return nil, err
	}

	if len(rows) < 2 { // Header + at least 1 row
		return nil, errors.New("csv file is empty or missing headers")
	}

	report := &ImportReport{}
	var products []domain.Product

	// Header mapping (simple index search)
	headers := rows[0]
	idxMap := make(map[string]int)
	for i, h := range headers {
		idxMap[strings.ToLower(strings.TrimSpace(h))] = i
	}

	required := []string{"name", "price"}
	for _, req := range required {
		if _, ok := idxMap[req]; !ok {
			return nil, fmt.Errorf("missing required header: %s", req)
		}
	}

	for i, row := range rows[1:] {
		report.TotalProcessed++
		lineNum := i + 2 // 1-based, +header

		// Parse Name
		name := strings.TrimSpace(row[idxMap["name"]])
		if name == "" {
			report.FailedCount++
			report.Errors = append(report.Errors, fmt.Sprintf("Line %d: name is required", lineNum))
			continue
		}

		// Parse Price (float or int string)
		priceStr := strings.TrimSpace(row[idxMap["price"]])
		priceFloat, err := strconv.ParseFloat(priceStr, 64)
		if err != nil {
			report.FailedCount++
			report.Errors = append(report.Errors, fmt.Sprintf("Line %d: invalid price '%s'", lineNum, priceStr))
			continue
		}
		priceCents := int64(priceFloat * 100)

		// Defaults / Optionals
		// var stock int64 // Removed for Dictionary Mode
		// if idx, ok := idxMap["stock"]; ok && idx < len(row) {
		// 	if s, err := strconv.ParseInt(strings.TrimSpace(row[idx]), 10, 64); err == nil {
		// 		stock = s
		// 	}
		// }

		var description string
		if idx, ok := idxMap["description"]; ok && idx < len(row) {
			description = strings.TrimSpace(row[idx])
		}

		var ean string
		if idx, ok := idxMap["ean"]; ok && idx < len(row) {
			ean = strings.TrimSpace(row[idx])
		}

		prod := domain.Product{
			ID:          uuid.Must(uuid.NewV7()),
			SellerID:    sellerID,
			Name:        name,
			Description: description,
			EANCode:     ean,
			PriceCents:  priceCents,
			// Stock & ExpiresAt removed from Catalog Dictionary
			CreatedAt: time.Now().UTC(),
			UpdatedAt: time.Now().UTC(),
		}
		products = append(products, prod)
	}

	if len(products) > 0 {
		if err := s.repo.BatchCreateProducts(ctx, products); err != nil {
			// If batch fails, we fail mostly everything?
			// Or we could implement line-by-line insert in repo.
			// For ImportProducts, failing the whole batch is acceptable if DB constraint fails.
			return nil, fmt.Errorf("batch insert failed: %w", err)
		}
	}

	return report, nil
}

func (s *Service) ListCategories(ctx context.Context) ([]string, error) {
	return s.repo.ListCategories(ctx)
}

func (s *Service) GetProductByEAN(ctx context.Context, ean string) (*domain.Product, error) {
	return s.repo.GetProductByEAN(ctx, ean)
}

func (s *Service) RegisterInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
	return s.repo.CreateInventoryItem(ctx, item)
}

func (s *Service) GetInventoryItem(ctx context.Context, id uuid.UUID) (*domain.InventoryItem, error) {
	return s.repo.GetInventoryItem(ctx, id)
}

func (s *Service) UpdateInventoryItem(ctx context.Context, item *domain.InventoryItem) error {
	return s.repo.UpdateInventoryItem(ctx, item)
}
