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"
)

type ContactService struct {
	queries *repository.Queries
	audit   *AuditService
}

func NewContactService(queries *repository.Queries, audit *AuditService) *ContactService {
	return &ContactService{queries: queries, audit: audit}
}

func (s *ContactService) Create(ctx context.Context, userID uuid.UUID, req CreateContactRequest) (*models.Contact, error) {
	if req.FirstName == nil && req.LastName == nil && req.Email == nil && req.Phone == nil {
		return nil, models.NewValidationError("at least one identifying field required")
	}
	if req.Email != nil {
		if err := utils.ValidateEmail(*req.Email); err != nil {
			return nil, err
		}
	}

	id := uuid.New()
	c, err := s.queries.CreateContact(ctx, repository.CreateContactParams{
		ID:        utils.ToPgUUID(id),
		OwnerID:   utils.ToPgUUID(userID),
		FirstName: utils.ToPgTextPtr(req.FirstName),
		LastName:  utils.ToPgTextPtr(req.LastName),
		Email:     utils.ToPgTextPtr(req.Email),
		Phone:     utils.ToPgTextPtr(req.Phone),
		Company:   utils.ToPgTextPtr(req.Company),
		Notes:     utils.ToPgTextPtr(req.Notes),
	})
	if err != nil {
		return nil, models.ErrInternal
	}

	s.audit.Log(ctx, "contact", id, "CREATE_CONTACT", userID)
	return contactFromDB(c), nil
}

func (s *ContactService) Get(ctx context.Context, userID uuid.UUID, contactID uuid.UUID) (*models.Contact, error) {
	c, err := s.queries.GetContactByID(ctx, repository.GetContactByIDParams{
		ID:      utils.ToPgUUID(contactID),
		OwnerID: utils.ToPgUUID(userID),
	})
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, models.ErrNotFound
		}
		return nil, models.ErrInternal
	}
	return contactFromDB(c), nil
}

func (s *ContactService) List(ctx context.Context, userID uuid.UUID, search *string, limit int, cursor string) ([]models.Contact, *string, error) {
	lim := utils.ClampLimit(limit)

	var cursorTime, cursorID interface{}
	p := repository.ListContactsParams{
		OwnerID: utils.ToPgUUID(userID),
		Search:  utils.ToPgTextPtr(search),
		Lim:     lim + 1,
	}

	if cursor != "" {
		ct, cid, err := utils.ParseCursor(cursor)
		if err != nil {
			return nil, nil, models.NewValidationError("invalid cursor")
		}
		p.CursorTime = utils.ToPgTimestamptzPtr(ct)
		p.CursorID = utils.ToPgUUID(*cid)
		cursorTime = ct
		cursorID = cid
	}
	_ = cursorTime
	_ = cursorID

	rows, err := s.queries.ListContacts(ctx, p)
	if err != nil {
		return nil, nil, models.ErrInternal
	}

	contacts := make([]models.Contact, 0, len(rows))
	for _, r := range rows {
		contacts = append(contacts, *contactFromDB(r))
	}

	if int32(len(contacts)) > lim {
		contacts = contacts[:lim]
		last := contacts[len(contacts)-1]
		c := utils.EncodeCursor(last.CreatedAt, last.ID)
		return contacts, &c, nil
	}

	return contacts, nil, nil
}

func (s *ContactService) Update(ctx context.Context, userID uuid.UUID, contactID uuid.UUID, req UpdateContactRequest) (*models.Contact, error) {
	c, err := s.queries.UpdateContact(ctx, repository.UpdateContactParams{
		ID:        utils.ToPgUUID(contactID),
		OwnerID:   utils.ToPgUUID(userID),
		FirstName: utils.ToPgTextPtr(req.FirstName),
		LastName:  utils.ToPgTextPtr(req.LastName),
		Email:     utils.ToPgTextPtr(req.Email),
		Phone:     utils.ToPgTextPtr(req.Phone),
		Company:   utils.ToPgTextPtr(req.Company),
		Notes:     utils.ToPgTextPtr(req.Notes),
	})
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, models.ErrNotFound
		}
		return nil, models.ErrInternal
	}

	s.audit.Log(ctx, "contact", contactID, "UPDATE_CONTACT", userID)
	return contactFromDB(c), nil
}

func (s *ContactService) Delete(ctx context.Context, userID uuid.UUID, contactID uuid.UUID) error {
	err := s.queries.SoftDeleteContact(ctx, repository.SoftDeleteContactParams{
		ID:      utils.ToPgUUID(contactID),
		OwnerID: utils.ToPgUUID(userID),
	})
	if err != nil {
		return models.ErrInternal
	}
	s.audit.Log(ctx, "contact", contactID, "DELETE_CONTACT", userID)
	return nil
}

func contactFromDB(v repository.Contact) *models.Contact {
	return &models.Contact{
		ID:        utils.FromPgUUID(v.ID),
		FirstName: utils.FromPgText(v.FirstName),
		LastName:  utils.FromPgText(v.LastName),
		Email:     utils.FromPgText(v.Email),
		Phone:     utils.FromPgText(v.Phone),
		Company:   utils.FromPgText(v.Company),
		Notes:     utils.FromPgText(v.Notes),
		CreatedAt: utils.FromPgTimestamptz(v.CreatedAt),
		UpdatedAt: utils.FromPgTimestamptz(v.UpdatedAt),
	}
}

type CreateContactRequest struct {
	FirstName *string
	LastName  *string
	Email     *string
	Phone     *string
	Company   *string
	Notes     *string
}

type UpdateContactRequest struct {
	FirstName *string
	LastName  *string
	Email     *string
	Phone     *string
	Company   *string
	Notes     *string
}
