package profissionais

import (
	"context"
	"errors"
	"fmt"

	"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 CreateProfissionalInput struct {
	Nome                 string   `json:"nome"`
	FuncaoProfissionalID string   `json:"funcao_profissional_id"`
	FuncoesIds           []string `json:"funcoes_ids"` // New field
	Endereco             *string  `json:"endereco"`
	Cidade               *string  `json:"cidade"`
	Uf                   *string  `json:"uf"`
	Whatsapp             *string  `json:"whatsapp"`
	CpfCnpjTitular       *string  `json:"cpf_cnpj_titular"`
	Banco                *string  `json:"banco"`
	Agencia              *string  `json:"agencia"`
	ContaPix             *string  `json:"conta_pix"`
	CarroDisponivel      *bool    `json:"carro_disponivel"`
	TemEstudio           *bool    `json:"tem_estudio"`
	QtdEstudio           *int     `json:"qtd_estudio"`
	TipoCartao           *string  `json:"tipo_cartao"`
	Observacao           *string  `json:"observacao"`
	QualTec              *int     `json:"qual_tec"`
	EducacaoSimpatia     *int     `json:"educacao_simpatia"`
	DesempenhoEvento     *int     `json:"desempenho_evento"`
	DispHorario          *int     `json:"disp_horario"`
	Media                *float64 `json:"media"`
	TabelaFree           *string  `json:"tabela_free"`
	ExtraPorEquipamento  *bool    `json:"extra_por_equipamento"`
	Equipamentos         *string  `json:"equipamentos"`
	Email                *string  `json:"email"`
	AvatarURL            *string  `json:"avatar_url"`
	TargetUserID         *string  `json:"target_user_id"` // Optional: For admin creation
}

func (s *Service) Create(ctx context.Context, userID string, input CreateProfissionalInput) (*generated.CadastroProfissionai, error) {
	finalUserID := userID
	if input.TargetUserID != nil && *input.TargetUserID != "" {
		finalUserID = *input.TargetUserID
	}

	usuarioUUID, err := uuid.Parse(finalUserID)
	if err != nil {
		return nil, errors.New("invalid usuario_id")
	}

	var funcaoUUID uuid.UUID
	var funcaoValid bool
	if input.FuncaoProfissionalID != "" {
		parsed, err := uuid.Parse(input.FuncaoProfissionalID)
		if err != nil {
			return nil, errors.New("invalid funcao_profissional_id")
		}
		funcaoUUID = parsed
		funcaoValid = true
	} else {
		funcaoValid = false
	}

	params := generated.CreateProfissionalParams{
		UsuarioID:            pgtype.UUID{Bytes: usuarioUUID, Valid: true},
		Nome:                 input.Nome,
		FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: funcaoValid},
		Endereco:             toPgText(input.Endereco),
		Cidade:               toPgText(input.Cidade),
		Uf:                   toPgText(input.Uf),
		Whatsapp:             toPgText(input.Whatsapp),
		CpfCnpjTitular:       toPgText(input.CpfCnpjTitular),
		Banco:                toPgText(input.Banco),
		Agencia:              toPgText(input.Agencia),
		ContaPix:             toPgText(input.ContaPix),
		CarroDisponivel:      toPgBool(input.CarroDisponivel),
		TemEstudio:           toPgBool(input.TemEstudio),
		QtdEstudio:           toPgInt4(input.QtdEstudio),
		TipoCartao:           toPgText(input.TipoCartao),
		Observacao:           toPgText(input.Observacao),
		QualTec:              toPgInt4(input.QualTec),
		EducacaoSimpatia:     toPgInt4(input.EducacaoSimpatia),
		DesempenhoEvento:     toPgInt4(input.DesempenhoEvento),
		DispHorario:          toPgInt4(input.DispHorario),
		Media:                toPgNumeric(input.Media),
		TabelaFree:           toPgText(input.TabelaFree),
		ExtraPorEquipamento:  toPgBool(input.ExtraPorEquipamento),
		Equipamentos:         toPgText(input.Equipamentos),
		Email:                toPgText(input.Email),
		AvatarUrl:            toPgText(input.AvatarURL),
	}

	prof, err := s.queries.CreateProfissional(ctx, params)
	if err != nil {
		return nil, err
	}

	// Insert multiple functions if provided
	if len(input.FuncoesIds) > 0 {
		for _, fid := range input.FuncoesIds {
			fUUID, err := uuid.Parse(fid)
			if err == nil {
				_ = s.queries.AddFunctionToProfessional(ctx, generated.AddFunctionToProfessionalParams{
					ProfissionalID: pgtype.UUID{Bytes: prof.ID.Bytes, Valid: true},
					FuncaoID:       pgtype.UUID{Bytes: fUUID, Valid: true},
				})
			}
		}
	} else if funcaoValid {
		// If no list provided but single ID is, insert that one too into junction
		_ = s.queries.AddFunctionToProfessional(ctx, generated.AddFunctionToProfessionalParams{
			ProfissionalID: pgtype.UUID{Bytes: prof.ID.Bytes, Valid: true},
			FuncaoID:       pgtype.UUID{Bytes: funcaoUUID, Valid: true},
		})
	}

	return &prof, nil
}

func (s *Service) List(ctx context.Context) ([]generated.ListProfissionaisRow, error) {
	return s.queries.ListProfissionais(ctx)
}

func (s *Service) GetByID(ctx context.Context, id string) (*generated.GetProfissionalByIDRow, error) {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return nil, errors.New("invalid id")
	}
	prof, err := s.queries.GetProfissionalByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true})
	if err != nil {
		return nil, err
	}
	return &prof, nil
}

type UpdateProfissionalInput struct {
	Nome                 string   `json:"nome"`
	FuncaoProfissionalID string   `json:"funcao_profissional_id"`
	FuncoesIds           []string `json:"funcoes_ids"` // New field
	Endereco             *string  `json:"endereco"`
	Cidade               *string  `json:"cidade"`
	Uf                   *string  `json:"uf"`
	Whatsapp             *string  `json:"whatsapp"`
	CpfCnpjTitular       *string  `json:"cpf_cnpj_titular"`
	Banco                *string  `json:"banco"`
	Agencia              *string  `json:"agencia"`
	ContaPix             *string  `json:"conta_pix"`
	CarroDisponivel      *bool    `json:"carro_disponivel"`
	TemEstudio           *bool    `json:"tem_estudio"`
	QtdEstudio           *int     `json:"qtd_estudio"`
	TipoCartao           *string  `json:"tipo_cartao"`
	Observacao           *string  `json:"observacao"`
	QualTec              *int     `json:"qual_tec"`
	EducacaoSimpatia     *int     `json:"educacao_simpatia"`
	DesempenhoEvento     *int     `json:"desempenho_evento"`
	DispHorario          *int     `json:"disp_horario"`
	Media                *float64 `json:"media"`
	TabelaFree           *string  `json:"tabela_free"`
	ExtraPorEquipamento  *bool    `json:"extra_por_equipamento"`
	Equipamentos         *string  `json:"equipamentos"`
	Email                *string  `json:"email"`
	AvatarURL            *string  `json:"avatar_url"`
}

func (s *Service) Update(ctx context.Context, id string, input UpdateProfissionalInput) (*generated.CadastroProfissionai, error) {
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return nil, errors.New("invalid id")
	}

	funcaoUUID, err := uuid.Parse(input.FuncaoProfissionalID)
	if err != nil {
		return nil, errors.New("invalid funcao_profissional_id")
	}

	params := generated.UpdateProfissionalParams{
		ID:                   pgtype.UUID{Bytes: uuidVal, Valid: true},
		Nome:                 input.Nome,
		FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: true},
		Endereco:             toPgText(input.Endereco),
		Cidade:               toPgText(input.Cidade),
		Uf:                   toPgText(input.Uf),
		Whatsapp:             toPgText(input.Whatsapp),
		CpfCnpjTitular:       toPgText(input.CpfCnpjTitular),
		Banco:                toPgText(input.Banco),
		Agencia:              toPgText(input.Agencia),
		ContaPix:             toPgText(input.ContaPix),
		CarroDisponivel:      toPgBool(input.CarroDisponivel),
		TemEstudio:           toPgBool(input.TemEstudio),
		QtdEstudio:           toPgInt4(input.QtdEstudio),
		TipoCartao:           toPgText(input.TipoCartao),
		Observacao:           toPgText(input.Observacao),
		QualTec:              toPgInt4(input.QualTec),
		EducacaoSimpatia:     toPgInt4(input.EducacaoSimpatia),
		DesempenhoEvento:     toPgInt4(input.DesempenhoEvento),
		DispHorario:          toPgInt4(input.DispHorario),
		Media:                toPgNumeric(input.Media),
		TabelaFree:           toPgText(input.TabelaFree),
		ExtraPorEquipamento:  toPgBool(input.ExtraPorEquipamento),
		Equipamentos:         toPgText(input.Equipamentos),
		Email:                toPgText(input.Email),
		AvatarUrl:            toPgText(input.AvatarURL),
	}

	prof, err := s.queries.UpdateProfissional(ctx, params)
	if err != nil {
		return nil, err
	}

	// Update functions logic
	// If input.FuncoesIds is provided (even empty), replace all.
	// If nil, maybe keep existing? For simplicity, let's assume if present we update.
	// Actually frontend should send full list.
	if input.FuncoesIds != nil {
		// Clear existing
		_ = s.queries.ClearProfessionalFunctions(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true})
		// Add new
		for _, fid := range input.FuncoesIds {
			fUUID, err := uuid.Parse(fid)
			if err == nil {
				_ = s.queries.AddFunctionToProfessional(ctx, generated.AddFunctionToProfessionalParams{
					ProfissionalID: pgtype.UUID{Bytes: uuidVal, Valid: true},
					FuncaoID:       pgtype.UUID{Bytes: fUUID, Valid: true},
				})
			}
		}
	} else if input.FuncaoProfissionalID != "" {
		// If legacy field matches, ensure it's in junction set too?
		// Or maybe we treat legacy ID as primary and sync it.
		// For now, let's just make sure at least one exists if provided.
		// But usually update sends all data.
	}

	return &prof, nil
}

func (s *Service) Delete(ctx context.Context, id string) error {
	fmt.Printf("[DEBUG] Deleting Professional: %s\n", id)
	uuidVal, err := uuid.Parse(id)
	if err != nil {
		return errors.New("invalid id")
	}

	// Get professional to find associated user
	prof, err := s.queries.GetProfissionalByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true})
	if err != nil {
		fmt.Printf("[DEBUG] Failed to get professional %s: %v\n", id, err)
		return err
	}

	fmt.Printf("[DEBUG] Prof Found:: ID=%s, UsuarioID.Valid=%v, UsuarioID=%s\n",
		uuid.UUID(prof.ID.Bytes).String(),
		prof.UsuarioID.Valid,
		uuid.UUID(prof.UsuarioID.Bytes).String())

	// Delete associated user first (ensures login access is revoked)
	if prof.UsuarioID.Valid {
		fmt.Printf("[DEBUG] Attempting to delete User ID: %s\n", uuid.UUID(prof.UsuarioID.Bytes).String())
		err = s.queries.DeleteUsuario(ctx, prof.UsuarioID)
		if err != nil {
			fmt.Printf("[DEBUG] Failed to delete User: %v\n", err)
			return err
		}
		fmt.Println("[DEBUG] User deleted successfully.")
	} else {
		fmt.Println("[DEBUG] UsuarioID is invalid/null. Skipping User deletion.")
	}

	// Delete professional profile
	fmt.Println("[DEBUG] Deleting Professional profile...")
	err = s.queries.DeleteProfissional(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true})
	if err != nil {
		fmt.Printf("[DEBUG] Failed to delete Professional: %v\n", err)
		return err
	}
	fmt.Println("[DEBUG] Professional deleted successfully.")

	return nil
}

// Helpers

func toPgText(s *string) pgtype.Text {
	if s == nil {
		return pgtype.Text{Valid: false}
	}
	return pgtype.Text{String: *s, Valid: true}
}

func toPgBool(b *bool) pgtype.Bool {
	if b == nil {
		return pgtype.Bool{Valid: false}
	}
	return pgtype.Bool{Bool: *b, Valid: true}
}

func toPgInt4(i *int) pgtype.Int4 {
	if i == nil {
		return pgtype.Int4{Valid: false}
	}
	return pgtype.Int4{Int32: int32(*i), Valid: true}
}

func toPgNumeric(f *float64) pgtype.Numeric {
	if f == nil {
		return pgtype.Numeric{Valid: false}
	}
	var n pgtype.Numeric
	if err := n.Scan(fmt.Sprintf("%f", *f)); err != nil {
		return pgtype.Numeric{Valid: false}
	}
	return n
}
