package agenda

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"photum-backend/internal/db/generated"

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

type Service struct {
	queries *generated.Queries
}

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

type CreateAgendaRequest struct {
	FotID                uuid.UUID `json:"fot_id"`
	DataEvento           time.Time `json:"data_evento"`
	TipoEventoID         uuid.UUID `json:"tipo_evento_id"`
	ObservacoesEvento    string    `json:"observacoes_evento"`
	LocalEvento          string    `json:"local_evento"`
	Endereco             string    `json:"endereco"`
	Horario              string    `json:"horario"`
	QtdFormandos         int32     `json:"qtd_formandos"`
	QtdFotografos        int32     `json:"qtd_fotografos"`
	QtdRecepcionistas    int32     `json:"qtd_recepcionistas"`
	QtdCinegrafistas     int32     `json:"qtd_cinegrafistas"`
	QtdEstudios          int32     `json:"qtd_estudios"`
	QtdPontoFoto         int32     `json:"qtd_ponto_foto"`
	QtdPontoID           int32     `json:"qtd_ponto_id"`
	QtdPontoDecorado     int32     `json:"qtd_ponto_decorado"`
	QtdPontosLed         int32     `json:"qtd_pontos_led"`
	QtdPlataforma360     int32     `json:"qtd_plataforma_360"`
	StatusProfissionais  string    `json:"status_profissionais"`
	FotoFaltante         int32     `json:"foto_faltante"`
	RecepFaltante        int32     `json:"recep_faltante"`
	CineFaltante         int32     `json:"cine_faltante"`
	LogisticaObservacoes string    `json:"logistica_observacoes"`
	PreVenda             bool      `json:"pre_venda"`
}

type Assignment struct {
	ProfessionalID string  `json:"professional_id"`
	Status         string  `json:"status"`
	MotivoRejeicao *string `json:"motivo_rejeicao"`
}

type AgendaResponse struct {
	generated.ListAgendasRow
	ParsedAssignments []Assignment `json:"assignments"`
}

func (s *Service) CalculateStatus(fotoFaltante, recepFaltante, cineFaltante int32) string {
	if fotoFaltante < 0 || recepFaltante < 0 || cineFaltante < 0 {
		return "ERRO"
	}

	sum := fotoFaltante + recepFaltante + cineFaltante
	if sum == 0 {
		return "OK"
	} else if sum > 0 {
		return "FALTA"
	}

	return "ERRO"
}

func (s *Service) Create(ctx context.Context, userID uuid.UUID, req CreateAgendaRequest) (generated.Agenda, error) {
	status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante)

	params := generated.CreateAgendaParams{
		FotID:                pgtype.UUID{Bytes: req.FotID, Valid: true},
		DataEvento:           pgtype.Date{Time: req.DataEvento, Valid: true},
		TipoEventoID:         pgtype.UUID{Bytes: req.TipoEventoID, Valid: true},
		ObservacoesEvento:    pgtype.Text{String: req.ObservacoesEvento, Valid: req.ObservacoesEvento != ""},
		LocalEvento:          pgtype.Text{String: req.LocalEvento, Valid: req.LocalEvento != ""},
		Endereco:             pgtype.Text{String: req.Endereco, Valid: req.Endereco != ""},
		Horario:              pgtype.Text{String: req.Horario, Valid: req.Horario != ""},
		QtdFormandos:         pgtype.Int4{Int32: req.QtdFormandos, Valid: true},
		QtdFotografos:        pgtype.Int4{Int32: req.QtdFotografos, Valid: true},
		QtdRecepcionistas:    pgtype.Int4{Int32: req.QtdRecepcionistas, Valid: true},
		QtdCinegrafistas:     pgtype.Int4{Int32: req.QtdCinegrafistas, Valid: true},
		QtdEstudios:          pgtype.Int4{Int32: req.QtdEstudios, Valid: true},
		QtdPontoFoto:         pgtype.Int4{Int32: req.QtdPontoFoto, Valid: true},
		QtdPontoID:           pgtype.Int4{Int32: req.QtdPontoID, Valid: true},
		QtdPontoDecorado:     pgtype.Int4{Int32: req.QtdPontoDecorado, Valid: true},
		QtdPontosLed:         pgtype.Int4{Int32: req.QtdPontosLed, Valid: true},
		QtdPlataforma360:     pgtype.Int4{Int32: req.QtdPlataforma360, Valid: true},
		StatusProfissionais:  pgtype.Text{String: status, Valid: true},
		FotoFaltante:         pgtype.Int4{Int32: req.FotoFaltante, Valid: true},
		RecepFaltante:        pgtype.Int4{Int32: req.RecepFaltante, Valid: true},
		CineFaltante:         pgtype.Int4{Int32: req.CineFaltante, Valid: true},
		LogisticaObservacoes: pgtype.Text{String: req.LogisticaObservacoes, Valid: req.LogisticaObservacoes != ""},
		PreVenda:             pgtype.Bool{Bool: req.PreVenda, Valid: true},
		UserID:               pgtype.UUID{Bytes: userID, Valid: true},
	}

	return s.queries.CreateAgenda(ctx, params)
}

func (s *Service) List(ctx context.Context, userID uuid.UUID, role string) ([]AgendaResponse, error) {
	var rows []generated.ListAgendasRow
	var err error

	// If role is CLIENT (cliente), filter by userID
	if role == "cliente" || role == "EVENT_OWNER" {
		listRows, err := s.queries.ListAgendasByUser(ctx, pgtype.UUID{Bytes: userID, Valid: true})
		if err != nil {
			return nil, err
		}
		// Convert ListAgendasByUserRow to ListAgendasRow manually
		for _, r := range listRows {
			rows = append(rows, generated.ListAgendasRow{
				ID:                    r.ID,
				UserID:                r.UserID,
				FotID:                 r.FotID,
				DataEvento:            r.DataEvento,
				TipoEventoID:          r.TipoEventoID,
				ObservacoesEvento:     r.ObservacoesEvento,
				LocalEvento:           r.LocalEvento,
				Endereco:              r.Endereco,
				Horario:               r.Horario,
				QtdFormandos:          r.QtdFormandos,
				QtdFotografos:         r.QtdFotografos,
				QtdRecepcionistas:     r.QtdRecepcionistas,
				QtdCinegrafistas:      r.QtdCinegrafistas,
				QtdEstudios:           r.QtdEstudios,
				QtdPontoFoto:          r.QtdPontoFoto,
				QtdPontoID:            r.QtdPontoID,
				QtdPontoDecorado:      r.QtdPontoDecorado,
				QtdPontosLed:          r.QtdPontosLed,
				QtdPlataforma360:      r.QtdPlataforma360,
				StatusProfissionais:   r.StatusProfissionais,
				FotoFaltante:          r.FotoFaltante,
				RecepFaltante:         r.RecepFaltante,
				CineFaltante:          r.CineFaltante,
				LogisticaObservacoes:  r.LogisticaObservacoes,
				PreVenda:              r.PreVenda,
				CriadoEm:              r.CriadoEm,
				AtualizadoEm:          r.AtualizadoEm,
				Status:                r.Status,
				FotNumero:             r.FotNumero,
				Instituicao:           r.Instituicao,
				CursoNome:             r.CursoNome,
				EmpresaNome:           r.EmpresaNome,
				EmpresaID:             r.EmpresaID,
				AnoSemestre:           r.AnoSemestre,
				ObservacoesFot:        r.ObservacoesFot,
				TipoEventoNome:        r.TipoEventoNome,
				AssignedProfessionals: r.AssignedProfessionals,
			})
		}
	} else {
		rows, err = s.queries.ListAgendas(ctx)
		if err != nil {
			return nil, err
		}
	}

	var response []AgendaResponse
	for _, row := range rows {
		var assignments []Assignment
		if row.AssignedProfessionals != nil {
			bytes, ok := row.AssignedProfessionals.([]byte)
			if !ok {
				str, ok := row.AssignedProfessionals.(string)
				if ok {
					bytes = []byte(str)
				}
			}
			if bytes != nil {
				json.Unmarshal(bytes, &assignments)
			}
		}
		response = append(response, AgendaResponse{
			ListAgendasRow:    row,
			ParsedAssignments: assignments,
		})
	}

	return response, nil
}

func (s *Service) Get(ctx context.Context, id uuid.UUID) (generated.Agenda, error) {
	return s.queries.GetAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
}

func (s *Service) Update(ctx context.Context, id uuid.UUID, req CreateAgendaRequest) (generated.Agenda, error) {
	if req.FotID == uuid.Nil {
		return generated.Agenda{}, fmt.Errorf("FOT ID inválido ou não informado")
	}
	if req.TipoEventoID == uuid.Nil {
		return generated.Agenda{}, fmt.Errorf("Tipo de Evento ID inválido ou não informado")
	}

	status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante)

	params := generated.UpdateAgendaParams{
		ID:                   pgtype.UUID{Bytes: id, Valid: true},
		FotID:                pgtype.UUID{Bytes: req.FotID, Valid: true},
		DataEvento:           pgtype.Date{Time: req.DataEvento, Valid: true},
		TipoEventoID:         pgtype.UUID{Bytes: req.TipoEventoID, Valid: true},
		ObservacoesEvento:    pgtype.Text{String: req.ObservacoesEvento, Valid: req.ObservacoesEvento != ""},
		LocalEvento:          pgtype.Text{String: req.LocalEvento, Valid: req.LocalEvento != ""},
		Endereco:             pgtype.Text{String: req.Endereco, Valid: req.Endereco != ""},
		Horario:              pgtype.Text{String: req.Horario, Valid: req.Horario != ""},
		QtdFormandos:         pgtype.Int4{Int32: req.QtdFormandos, Valid: true},
		QtdFotografos:        pgtype.Int4{Int32: req.QtdFotografos, Valid: true},
		QtdRecepcionistas:    pgtype.Int4{Int32: req.QtdRecepcionistas, Valid: true},
		QtdCinegrafistas:     pgtype.Int4{Int32: req.QtdCinegrafistas, Valid: true},
		QtdEstudios:          pgtype.Int4{Int32: req.QtdEstudios, Valid: true},
		QtdPontoFoto:         pgtype.Int4{Int32: req.QtdPontoFoto, Valid: true},
		QtdPontoID:           pgtype.Int4{Int32: req.QtdPontoID, Valid: true},
		QtdPontoDecorado:     pgtype.Int4{Int32: req.QtdPontoDecorado, Valid: true},
		QtdPontosLed:         pgtype.Int4{Int32: req.QtdPontosLed, Valid: true},
		QtdPlataforma360:     pgtype.Int4{Int32: req.QtdPlataforma360, Valid: true},
		StatusProfissionais:  pgtype.Text{String: status, Valid: true},
		FotoFaltante:         pgtype.Int4{Int32: req.FotoFaltante, Valid: true},
		RecepFaltante:        pgtype.Int4{Int32: req.RecepFaltante, Valid: true},
		CineFaltante:         pgtype.Int4{Int32: req.CineFaltante, Valid: true},
		LogisticaObservacoes: pgtype.Text{String: req.LogisticaObservacoes, Valid: req.LogisticaObservacoes != ""},
		PreVenda:             pgtype.Bool{Bool: req.PreVenda, Valid: true},
	}
	return s.queries.UpdateAgenda(ctx, params)
}

func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
	return s.queries.DeleteAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
}

func (s *Service) AssignProfessional(ctx context.Context, agendaID uuid.UUID, profID uuid.UUID) error {
	params := generated.AssignProfessionalParams{
		AgendaID:       pgtype.UUID{Bytes: agendaID, Valid: true},
		ProfissionalID: pgtype.UUID{Bytes: profID, Valid: true},
	}
	return s.queries.AssignProfessional(ctx, params)
}

func (s *Service) RemoveProfessional(ctx context.Context, agendaID uuid.UUID, profID uuid.UUID) error {
	params := generated.RemoveProfessionalParams{
		AgendaID:       pgtype.UUID{Bytes: agendaID, Valid: true},
		ProfissionalID: pgtype.UUID{Bytes: profID, Valid: true},
	}
	return s.queries.RemoveProfessional(ctx, params)
}

func (s *Service) GetAgendaProfessionals(ctx context.Context, agendaID uuid.UUID) ([]generated.GetAgendaProfessionalsRow, error) {
	return s.queries.GetAgendaProfessionals(ctx, pgtype.UUID{Bytes: agendaID, Valid: true})
}

func (s *Service) UpdateStatus(ctx context.Context, agendaID uuid.UUID, status string) (generated.Agenda, error) {
	params := generated.UpdateAgendaStatusParams{
		ID:     pgtype.UUID{Bytes: agendaID, Valid: true},
		Status: pgtype.Text{String: status, Valid: true},
	}
	return s.queries.UpdateAgendaStatus(ctx, params)
}

func (s *Service) UpdateAssignmentStatus(ctx context.Context, agendaID, professionalID uuid.UUID, status string, reason string) error {
	// Conflict Validation on Accept
	if status == "ACEITO" {
		// 1. Get Current Agenda to know Date/Time
		agenda, err := s.queries.GetAgenda(ctx, pgtype.UUID{Bytes: agendaID, Valid: true})
		if err != nil {
			return fmt.Errorf("erro ao buscar agenda para validação: %v", err)
		}

		if agenda.DataEvento.Valid {
			// 2. Check for other confirmed events on the same date
			// Exclude current agenda ID from check
			conflicts, err := s.queries.CheckProfessionalBusyDate(ctx, generated.CheckProfessionalBusyDateParams{
				ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
				DataEvento:     agenda.DataEvento,
				ID:             pgtype.UUID{Bytes: agendaID, Valid: true},
			})
			if err != nil {
				return fmt.Errorf("erro ao verificar disponibilidade: %v", err)
			}

			if len(conflicts) > 0 {
				// 3. Check time overlap
				currentStart := parseTimeMinutes(agenda.Horario.String)
				currentEnd := currentStart + 240 // Assume 4 hours duration

				for _, c := range conflicts {
					conflictStart := parseTimeMinutes(c.Horario.String)
					conflictEnd := conflictStart + 240

					// Check overlap: StartA < EndB && StartB < EndA
					if currentStart < conflictEnd && conflictStart < currentEnd {
						return fmt.Errorf("conflito de horário: profissional já confirmou presença em outro evento às %s", c.Horario.String)
					}
				}
			}
		}
	}

	params := generated.UpdateAssignmentStatusParams{
		AgendaID:       pgtype.UUID{Bytes: agendaID, Valid: true},
		ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
		Status:         pgtype.Text{String: status, Valid: true},
		MotivoRejeicao: pgtype.Text{String: reason, Valid: reason != ""},
	}
	_, err := s.queries.UpdateAssignmentStatus(ctx, params)
	return err
}

// Helper for time parsing (HH:MM)
func parseTimeMinutes(t string) int {
	if len(t) < 5 {
		return 0 // Default or Error
	}
	h, _ := strconv.Atoi(t[0:2])
	m, _ := strconv.Atoi(t[3:5])
	return h*60 + m
}

func (s *Service) UpdateAssignmentPosition(ctx context.Context, agendaID, professionalID uuid.UUID, posicao string) error {
	params := generated.UpdateAssignmentPositionParams{
		AgendaID:       pgtype.UUID{Bytes: agendaID, Valid: true},
		ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
		Posicao:        pgtype.Text{String: posicao, Valid: true},
	}
	_, err := s.queries.UpdateAssignmentPosition(ctx, params)
	return err
}

func (s *Service) ListAvailableProfessionals(ctx context.Context, date time.Time) ([]generated.ListAvailableProfessionalsForDateRow, error) {
	return s.queries.ListAvailableProfessionalsForDate(ctx, pgtype.Date{Time: date, Valid: true})
}
