package mail

import (
	"context"
	"errors"

	"github.com/jackc/pgx/v5"

	"github.com/ultisuite/ulti-backend/internal/api/query"
	"github.com/ultisuite/ulti-backend/internal/securityaudit"
)

type SignaturesList struct {
	Signatures []map[string]any     `json:"signatures"`
	Pagination query.PaginationMeta `json:"pagination,omitempty"`
}

func scanSignature(id, name, html string, sortOrder int, createdAt, updatedAt any) map[string]any {
	return map[string]any{
		"id":         id,
		"name":       name,
		"html":       html,
		"sort_order": sortOrder,
		"created_at": createdAt,
		"updated_at": updatedAt,
	}
}

func (s *Service) verifySignatureOwnership(ctx context.Context, externalID, signatureID string) error {
	var exists bool
	err := s.db.QueryRow(ctx, `
		SELECT EXISTS(
			SELECT 1 FROM mail_signatures ms
			JOIN users u ON ms.user_id = u.id
			WHERE ms.id = $1 AND u.external_id = $2
		)
	`, signatureID, externalID).Scan(&exists)
	if err != nil {
		return err
	}
	if !exists {
		return ErrNotFound
	}
	return nil
}

func (s *Service) ListSignatures(ctx context.Context, externalID string, params query.ListParams) (SignaturesList, error) {
	var total int64
	if err := s.db.QueryRow(ctx, `
		SELECT COUNT(*) FROM mail_signatures ms
		JOIN users u ON ms.user_id = u.id
		WHERE u.external_id = $1
	`, externalID).Scan(&total); err != nil {
		return SignaturesList{}, err
	}

	rows, err := s.db.Query(ctx, `
		SELECT ms.id, ms.name, ms.html, ms.sort_order, ms.created_at, ms.updated_at
		FROM mail_signatures ms
		JOIN users u ON ms.user_id = u.id
		WHERE u.external_id = $1
		ORDER BY ms.sort_order ASC, ms.created_at ASC
		LIMIT $2 OFFSET $3
	`, externalID, params.Limit(), params.Offset())
	if err != nil {
		return SignaturesList{}, err
	}
	defer rows.Close()

	signatures := make([]map[string]any, 0)
	for rows.Next() {
		var id, name, html string
		var sortOrder int
		var createdAt, updatedAt any
		if err := rows.Scan(&id, &name, &html, &sortOrder, &createdAt, &updatedAt); err != nil {
			return SignaturesList{}, err
		}
		signatures = append(signatures, scanSignature(id, name, html, sortOrder, createdAt, updatedAt))
	}
	if err := rows.Err(); err != nil {
		return SignaturesList{}, err
	}

	return SignaturesList{
		Signatures: signatures,
		Pagination: params.Meta(&total),
	}, nil
}

func (s *Service) GetSignature(ctx context.Context, externalID, signatureID string) (map[string]any, error) {
	var id, name, html string
	var sortOrder int
	var createdAt, updatedAt any
	err := s.db.QueryRow(ctx, `
		SELECT ms.id, ms.name, ms.html, ms.sort_order, ms.created_at, ms.updated_at
		FROM mail_signatures ms
		JOIN users u ON ms.user_id = u.id
		WHERE ms.id = $1 AND u.external_id = $2
	`, signatureID, externalID).Scan(&id, &name, &html, &sortOrder, &createdAt, &updatedAt)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, err
	}
	return scanSignature(id, name, html, sortOrder, createdAt, updatedAt), nil
}

func (s *Service) CreateSignature(ctx context.Context, externalID string, req *createSignatureRequest) (string, error) {
	userID, err := s.ResolveUserID(ctx, externalID)
	if err != nil {
		return "", err
	}

	var id string
	err = s.db.QueryRow(ctx, `
		INSERT INTO mail_signatures (user_id, name, html)
		VALUES ($1, $2, $3)
		RETURNING id
	`, userID, req.Name, req.HTML).Scan(&id)
	if err != nil {
		return "", err
	}
	return id, nil
}

func (s *Service) UpdateSignature(ctx context.Context, externalID, signatureID string, req *updateSignatureRequest) error {
	result, err := s.db.Exec(ctx, `
		UPDATE mail_signatures ms SET
			name = $1, html = $2, updated_at = NOW()
		FROM users u
		WHERE ms.id = $3 AND ms.user_id = u.id AND u.external_id = $4
	`, req.Name, req.HTML, signatureID, externalID)
	if err != nil {
		return err
	}
	if result.RowsAffected() == 0 {
		return ErrNotFound
	}
	return nil
}

func (s *Service) DeleteSignature(ctx context.Context, externalID, signatureID string) error {
	result, err := s.db.Exec(ctx, `
		DELETE FROM mail_signatures ms
		USING users u
		WHERE ms.id = $1 AND ms.user_id = u.id AND u.external_id = $2
	`, signatureID, externalID)
	if err != nil {
		return err
	}
	if result.RowsAffected() == 0 {
		return ErrNotFound
	}
	if s.audit != nil {
		s.audit.Log(ctx, externalID, securityaudit.ActionCriticalDeletion, map[string]any{
			"target": "mail_signature", "signature_id": signatureID,
		})
	}
	return nil
}
