package service

import (
	"context"

	"github.com/calendarapi/internal/models"
	"github.com/calendarapi/internal/repository"
	"github.com/calendarapi/internal/utils"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

type UserService struct {
	pool    *pgxpool.Pool
	queries *repository.Queries
	audit   *AuditService
}

func NewUserService(pool *pgxpool.Pool, queries *repository.Queries, audit *AuditService) *UserService {
	return &UserService{pool: pool, queries: queries, audit: audit}
}

func (s *UserService) GetMe(ctx context.Context, userID uuid.UUID) (*models.User, error) {
	u, err := s.queries.GetUserByID(ctx, utils.ToPgUUID(userID))
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, models.ErrNotFound
		}
		return nil, models.ErrInternal
	}
	user := userFromIDRow(u)
	return &user, nil
}

type UserUpdateInput struct {
	Timezone                    *string
	WeekStartDay                *int
	DateFormat                  *string
	TimeFormat                  *string
	DefaultEventDurationMinutes *int
	DefaultReminderMinutes      *int
	ShowWeekends                *bool
	WorkingHoursStart           *string
	WorkingHoursEnd             *string
	NotificationsEmail          *bool
}

func (s *UserService) Update(ctx context.Context, userID uuid.UUID, in *UserUpdateInput) (*models.User, error) {
	if in == nil {
		in = &UserUpdateInput{}
	}
	if in.Timezone != nil {
		if err := utils.ValidateTimezone(*in.Timezone); err != nil {
			return nil, err
		}
	}

	u, err := s.queries.UpdateUser(ctx, repository.UpdateUserParams{
		ID:                          utils.ToPgUUID(userID),
		Timezone:                    utils.ToPgTextPtr(in.Timezone),
		WeekStartDay:                utils.ToPgInt2Ptr(in.WeekStartDay),
		DateFormat:                  utils.ToPgTextPtr(in.DateFormat),
		TimeFormat:                  utils.ToPgTextPtr(in.TimeFormat),
		DefaultEventDurationMinutes: utils.ToPgInt4Ptr(in.DefaultEventDurationMinutes),
		DefaultReminderMinutes:      utils.ToPgInt4Ptr(in.DefaultReminderMinutes),
		ShowWeekends:                utils.ToPgBoolPtr(in.ShowWeekends),
		WorkingHoursStart:           utils.ToPgTextPtr(in.WorkingHoursStart),
		WorkingHoursEnd:             utils.ToPgTextPtr(in.WorkingHoursEnd),
		NotificationsEmail:         utils.ToPgBoolPtr(in.NotificationsEmail),
	})
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, models.ErrNotFound
		}
		return nil, models.ErrInternal
	}
	user := userFromUpdateRow(u)
	return &user, nil
}

func (s *UserService) Delete(ctx context.Context, userID uuid.UUID) error {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return models.ErrInternal
	}
	defer tx.Rollback(ctx)

	qtx := s.queries.WithTx(tx)
	pgID := utils.ToPgUUID(userID)

	if err := qtx.SoftDeleteContactsByOwner(ctx, pgID); err != nil {
		return models.ErrInternal
	}
	if err := qtx.SoftDeleteEventsByCreator(ctx, pgID); err != nil {
		return models.ErrInternal
	}
	if err := qtx.SoftDeleteCalendarsByOwner(ctx, pgID); err != nil {
		return models.ErrInternal
	}
	if err := qtx.RevokeAllUserAPIKeys(ctx, pgID); err != nil {
		return models.ErrInternal
	}
	if err := qtx.RevokeAllUserRefreshTokens(ctx, pgID); err != nil {
		return models.ErrInternal
	}
	if err := qtx.SoftDeleteUser(ctx, pgID); err != nil {
		return models.ErrInternal
	}

	if err := tx.Commit(ctx); err != nil {
		return models.ErrInternal
	}

	s.audit.Log(ctx, "user", userID, "DELETE_USER", userID)
	return nil
}
