package cadastro_fot

import (
	"context"
	"errors"
	"strconv"
	"strings"

	"photum-backend/internal/db/generated"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgtype"
)

type Service struct {
	queries *generated.Queries
}

func NewService(queries *generated.Queries) *Service {
	return &Service{queries: queries}
}

type CreateInput struct {
	Fot            string  `json:"fot"`
	EmpresaID      string  `json:"empresa_id"`
	CursoID        string  `json:"curso_id"`
	AnoFormaturaID string  `json:"ano_formatura_id"`
	Instituicao    string  `json:"instituicao"`
	Cidade         string  `json:"cidade"`
	Estado         string  `json:"estado"`
	Observacoes    string  `json:"observacoes"`
	GastosCaptacao float64 `json:"gastos_captacao"`
	PreVenda       bool    `json:"pre_venda"`
}

func (s *Service) Create(ctx context.Context, input CreateInput, regiao string) (*generated.CadastroFot, error) {
	empresaUUID, _ := uuid.Parse(input.EmpresaID)
	cursoUUID, _ := uuid.Parse(input.CursoID)
	anoUUID, _ := uuid.Parse(input.AnoFormaturaID)

	res, err := s.queries.CreateCadastroFot(ctx, generated.CreateCadastroFotParams{
		Fot:            input.Fot,
		EmpresaID:      pgtype.UUID{Bytes: empresaUUID, Valid: true},
		CursoID:        pgtype.UUID{Bytes: cursoUUID, Valid: true},
		AnoFormaturaID: pgtype.UUID{Bytes: anoUUID, Valid: true},
		Instituicao:    pgtype.Text{String: input.Instituicao, Valid: true},
		Cidade:         pgtype.Text{String: input.Cidade, Valid: true},
		Estado:         pgtype.Text{String: input.Estado, Valid: true},
		Observacoes:    pgtype.Text{String: input.Observacoes, Valid: true},
		GastosCaptacao: toPgNumeric(input.GastosCaptacao),
		PreVenda:       pgtype.Bool{Bool: input.PreVenda, Valid: true},
		Regiao:         pgtype.Text{String: regiao, Valid: true},
	})
	if err != nil {
		return nil, err
	}
	return &res, nil
}

func (s *Service) List(ctx context.Context, regiao string) ([]generated.ListCadastroFotRow, error) {
	return s.queries.ListCadastroFot(ctx, pgtype.Text{String: regiao, Valid: true})
}

func (s *Service) ListByEmpresa(ctx context.Context, empresaID string, regiao string) ([]generated.ListCadastroFotByEmpresaRow, error) {
	uuidVal, err := uuid.Parse(empresaID)
	if err != nil {
		return nil, errors.New("invalid empresa_id")
	}
	// Note: ListCadastroFotByEmpresaRow is nearly identical to ListCadastroFotRow but we use the generated type
	return s.queries.ListCadastroFotByEmpresa(ctx, generated.ListCadastroFotByEmpresaParams{
		EmpresaID: pgtype.UUID{Bytes: uuidVal, Valid: true},
		Regiao:    pgtype.Text{String: regiao, Valid: true},
	})
}

func (s *Service) GetByID(ctx context.Context, id string, regiao string) (*generated.GetCadastroFotByIDRow, error) {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return nil, errors.New("invalid id")
	}
	item, err := s.queries.GetCadastroFotByID(ctx, generated.GetCadastroFotByIDParams{
		ID:     pgtype.UUID{Bytes: uuidVal, Valid: true},
		Regiao: pgtype.Text{String: regiao, Valid: true},
	})
	return &item, err
}

func (s *Service) Update(ctx context.Context, id string, input CreateInput, regiao string) (*generated.CadastroFot, error) {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return nil, errors.New("invalid id")
	}
	empresaUUID, _ := uuid.Parse(input.EmpresaID)
	cursoUUID, _ := uuid.Parse(input.CursoID)
	anoUUID, _ := uuid.Parse(input.AnoFormaturaID)

	item, err := s.queries.UpdateCadastroFot(ctx, generated.UpdateCadastroFotParams{
		ID:             pgtype.UUID{Bytes: uuidVal, Valid: true},
		Fot:            input.Fot,
		EmpresaID:      pgtype.UUID{Bytes: empresaUUID, Valid: true},
		CursoID:        pgtype.UUID{Bytes: cursoUUID, Valid: true},
		AnoFormaturaID: pgtype.UUID{Bytes: anoUUID, Valid: true},
		Instituicao:    pgtype.Text{String: input.Instituicao, Valid: true},
		Cidade:         pgtype.Text{String: input.Cidade, Valid: true},
		Estado:         pgtype.Text{String: input.Estado, Valid: true},
		Observacoes:    pgtype.Text{String: input.Observacoes, Valid: true},
		GastosCaptacao: toPgNumeric(input.GastosCaptacao),
		PreVenda:       pgtype.Bool{Bool: input.PreVenda, Valid: true},
		Regiao:         pgtype.Text{String: regiao, Valid: true},
	})
	return &item, err
}

func (s *Service) Delete(ctx context.Context, id string, regiao string) error {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return errors.New("invalid id")
	}
	return s.queries.DeleteCadastroFot(ctx, generated.DeleteCadastroFotParams{
		ID:     pgtype.UUID{Bytes: uuidVal, Valid: true},
		Regiao: pgtype.Text{String: regiao, Valid: true},
	})
}

// Helper to convert float to numeric robustly
func toPgNumeric(f float64) pgtype.Numeric {
	var n pgtype.Numeric
	s := strconv.FormatFloat(f, 'f', -1, 64)
	if err := n.Scan(s); err != nil {
		return pgtype.Numeric{Valid: false}
	}
	return n
}

// ToggleFinalizada sets the finalized status of a FOT
func (s *Service) ToggleFinalizada(ctx context.Context, id string, finalizada bool, regiao string) (*generated.CadastroFot, error) {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return nil, errors.New("invalid id")
	}
	item, err := s.queries.ToggleFinalizada(ctx, generated.ToggleFinalizadaParams{
		ID:         pgtype.UUID{Bytes: uuidVal, Valid: true},
		Finalizada: pgtype.Bool{Bool: finalizada, Valid: true},
		Regiao:     pgtype.Text{String: regiao, Valid: true},
	})
	if err != nil {
		return nil, err
	}
	return &item, nil
}

// ImportInput for Batch Import
type ImportInput struct {
	Fot               string  `json:"fot"`
	EmpresaNome       string  `json:"empresa_nome"`
	CursoNome         string  `json:"curso_nome"`
	AnoFormaturaLabel string  `json:"ano_formatura_label"`
	Instituicao       string  `json:"instituicao"`
	Cidade            string  `json:"cidade"`
	Estado            string  `json:"estado"`
	Observacoes       string  `json:"observacoes"`
	GastosCaptacao    float64 `json:"gastos_captacao"`
	PreVenda          bool    `json:"pre_venda"`
}

type ImportResult struct {
	SuccessCount int
	Errors       []string
}

func (s *Service) BatchImport(ctx context.Context, items []ImportInput, regiao string) ImportResult {
	result := ImportResult{Errors: []string{}}

	for i, item := range items {
		// 1. Resolve Empresa
		empresa, err := s.queries.GetEmpresaByNome(ctx, generated.GetEmpresaByNomeParams{
			Nome:   item.EmpresaNome,
			Regiao: pgtype.Text{String: regiao, Valid: true},
		})
		var empresaID pgtype.UUID
		if err != nil {
			// Try Create
			newEmp, errCreate := s.queries.CreateEmpresa(ctx, generated.CreateEmpresaParams{
				Nome:   item.EmpresaNome,
				Regiao: pgtype.Text{String: regiao, Valid: true},
			})
			if errCreate != nil {
				// Retry Get if duplicate
				if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
					empresaRetry, errRetry := s.queries.GetEmpresaByNome(ctx, generated.GetEmpresaByNomeParams{
						Nome:   item.EmpresaNome,
						Regiao: pgtype.Text{String: regiao, Valid: true},
					})
					if errRetry != nil {
						result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving empresa (retry): "+errRetry.Error())
						continue
					}
					empresaID = empresaRetry.ID
				} else {
					result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating empresa: "+errCreate.Error())
					continue
				}
			} else {
				empresaID = newEmp.ID
			}
		} else {
			empresaID = empresa.ID
		}

		// 2. Resolve Curso
		curso, err := s.queries.GetCursoByNome(ctx, generated.GetCursoByNomeParams{
			Nome:   item.CursoNome,
			Regiao: pgtype.Text{String: regiao, Valid: true},
		})
		var cursoID pgtype.UUID
		if err != nil {
			newCurso, errCreate := s.queries.CreateCurso(ctx, generated.CreateCursoParams{
				Nome:   item.CursoNome,
				Regiao: pgtype.Text{String: regiao, Valid: true},
			})
			if errCreate != nil {
				if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
					cursoRetry, errRetry := s.queries.GetCursoByNome(ctx, generated.GetCursoByNomeParams{
						Nome:   item.CursoNome,
						Regiao: pgtype.Text{String: regiao, Valid: true},
					})
					if errRetry != nil {
						result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving curso (retry): "+errRetry.Error())
						continue
					}
					cursoID = cursoRetry.ID
				} else {
					result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating curso: "+errCreate.Error())
					continue
				}
			} else {
				cursoID = newCurso.ID
			}
		} else {
			cursoID = curso.ID
		}

		// 3. Resolve Ano Formatura
		ano, err := s.queries.GetAnoFormaturaByNome(ctx, generated.GetAnoFormaturaByNomeParams{
			AnoSemestre: item.AnoFormaturaLabel,
			Regiao:      pgtype.Text{String: regiao, Valid: true},
		})
		var anoID pgtype.UUID
		if err != nil {
			newAno, errCreate := s.queries.CreateAnoFormatura(ctx, generated.CreateAnoFormaturaParams{
				AnoSemestre: item.AnoFormaturaLabel,
				Regiao:      pgtype.Text{String: regiao, Valid: true},
			})
			if errCreate != nil {
				if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
					anoRetry, errRetry := s.queries.GetAnoFormaturaByNome(ctx, generated.GetAnoFormaturaByNomeParams{
						AnoSemestre: item.AnoFormaturaLabel,
						Regiao:      pgtype.Text{String: regiao, Valid: true},
					})
					if errRetry != nil {
						result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving ano (retry): "+errRetry.Error())
						continue
					}
					anoID = anoRetry.ID
				} else {
					result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating ano: "+errCreate.Error())
					continue
				}
			} else {
				anoID = newAno.ID
			}
		} else {
			anoID = ano.ID
		}

		// 4. Insert Cadastro FOT
		// Check if exists? Or try create and catch duplicate
		// Ideally we update if exists? Let's just try Create for now.
		_, err = s.queries.CreateCadastroFot(ctx, generated.CreateCadastroFotParams{
			Fot:            item.Fot,
			EmpresaID:      empresaID,
			CursoID:        cursoID,
			AnoFormaturaID: anoID,
			Instituicao:    pgtype.Text{String: item.Instituicao, Valid: true},
			Cidade:         pgtype.Text{String: item.Cidade, Valid: true},
			Estado:         pgtype.Text{String: item.Estado, Valid: true},
			Observacoes:    pgtype.Text{String: item.Observacoes, Valid: true},
			GastosCaptacao: toPgNumeric(item.GastosCaptacao),
			PreVenda:       pgtype.Bool{Bool: item.PreVenda, Valid: true},
			Regiao:         pgtype.Text{String: regiao, Valid: true},
		})

		if err != nil {
			result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error inserting FOT "+item.Fot+": "+err.Error())
		} else {
			result.SuccessCount++
		}
	}

	return result
}
