package services

import (
	"context"
	"database/sql"
	"errors"

	"github.com/rede5/gohorsejobs/backend/internal/models"
)

type TicketService struct {
	DB *sql.DB
}

func NewTicketService(db *sql.DB) *TicketService {
	return &TicketService{DB: db}
}

func (s *TicketService) CreateTicket(ctx context.Context, userID string, subject, priority string) (*models.Ticket, error) {
	if priority == "" {
		priority = "medium"
	}
	query := `
		INSERT INTO tickets (user_id, subject, status, priority, created_at, updated_at)
		VALUES ($1, $2, 'open', $3, NOW(), NOW())
		RETURNING id, user_id, subject, status, priority, created_at, updated_at
	`
	var t models.Ticket
	err := s.DB.QueryRowContext(ctx, query, userID, subject, priority).Scan(
		&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}
	return &t, nil
}

func (s *TicketService) ListTickets(ctx context.Context, userID string) ([]models.Ticket, error) {
	query := `
		SELECT id, user_id, subject, status, priority, created_at, updated_at
		FROM tickets
		WHERE user_id = $1
		ORDER BY updated_at DESC
	`
	rows, err := s.DB.QueryContext(ctx, query, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	tickets := []models.Ticket{}
	for rows.Next() {
		var t models.Ticket
		if err := rows.Scan(
			&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
		); err != nil {
			return nil, err
		}
		tickets = append(tickets, t)
	}
	return tickets, nil
}

func (s *TicketService) GetTicket(ctx context.Context, ticketID string, userID string, isAdmin bool) (*models.Ticket, []models.TicketMessage, error) {
	// 1. Get Ticket
	queryTicket := `
		SELECT id, user_id, subject, status, priority, created_at, updated_at
		FROM tickets
		WHERE id = $1
	`
	args := []any{ticketID}
	if !isAdmin {
		queryTicket += " AND user_id = $2"
		args = append(args, userID)
	}

	var t models.Ticket
	err := s.DB.QueryRowContext(ctx, queryTicket, args...).Scan(
		&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
	)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil, errors.New("ticket not found")
		}
		return nil, nil, err
	}

	// 2. Get Messages
	queryMsgs := `
		SELECT id, ticket_id, user_id, message, created_at
		FROM ticket_messages
		WHERE ticket_id = $1
		ORDER BY created_at ASC
	`
	rows, err := s.DB.QueryContext(ctx, queryMsgs, ticketID)
	if err != nil {
		return nil, nil, err
	}
	defer rows.Close()

	messages := []models.TicketMessage{}
	for rows.Next() {
		var m models.TicketMessage
		if err := rows.Scan(
			&m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt,
		); err != nil {
			return nil, nil, err
		}
		messages = append(messages, m)
	}

	return &t, messages, nil
}

func (s *TicketService) AddMessage(ctx context.Context, ticketID string, userID string, message string, isAdmin bool) (*models.TicketMessage, error) {
	// Verify ticket ownership first (or admin access)
	var count int
	query := "SELECT COUNT(*) FROM tickets WHERE id = $1 AND user_id = $2"
	args := []any{ticketID, userID}
	if isAdmin {
		query = "SELECT COUNT(*) FROM tickets WHERE id = $1"
		args = []any{ticketID}
	}
	err := s.DB.QueryRowContext(ctx, query, args...).Scan(&count)
	if err != nil {
		return nil, err
	}
	if count == 0 {
		return nil, errors.New("ticket not found")
	}

	query = `
		INSERT INTO ticket_messages (ticket_id, user_id, message, created_at)
		VALUES ($1, $2, $3, NOW())
		RETURNING id, ticket_id, user_id, message, created_at
	`
	var m models.TicketMessage
	err = s.DB.QueryRowContext(ctx, query, ticketID, userID, message).Scan(
		&m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt,
	)
	if err != nil {
		return nil, err
	}

	// Update ticket updated_at
	_, _ = s.DB.ExecContext(ctx, "UPDATE tickets SET updated_at = NOW() WHERE id = $1", ticketID)

	return &m, nil
}

// UpdateTicket updates a ticket's status and/or priority
func (s *TicketService) UpdateTicket(ctx context.Context, ticketID string, userID string, status *string, priority *string, isAdmin bool) (*models.Ticket, error) {
	// Verify ownership (or admin access)
	var ownerID string
	err := s.DB.QueryRowContext(ctx, "SELECT user_id FROM tickets WHERE id = $1", ticketID).Scan(&ownerID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, errors.New("ticket not found")
		}
		return nil, err
	}

	// Only owner or admin can update
	if ownerID != userID && !isAdmin {
		return nil, errors.New("unauthorized")
	}

	// Build dynamic update
	setClauses := []string{"updated_at = NOW()"}
	args := []interface{}{}
	argIdx := 1

	if status != nil {
		setClauses = append(setClauses, "status = $"+string(rune('0'+argIdx)))
		args = append(args, *status)
		argIdx++
	}
	if priority != nil {
		setClauses = append(setClauses, "priority = $"+string(rune('0'+argIdx)))
		args = append(args, *priority)
		argIdx++
	}

	args = append(args, ticketID)
	query := "UPDATE tickets SET " + joinStrings(setClauses, ", ") + " WHERE id = $" + string(rune('0'+argIdx)) + " RETURNING id, user_id, subject, status, priority, created_at, updated_at"

	var t models.Ticket
	err = s.DB.QueryRowContext(ctx, query, args...).Scan(
		&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
	)
	if err != nil {
		return nil, err
	}
	return &t, nil
}

// CloseTicket is a convenience method to close a ticket
func (s *TicketService) CloseTicket(ctx context.Context, ticketID string, userID string, isAdmin bool) (*models.Ticket, error) {
	status := "closed"
	return s.UpdateTicket(ctx, ticketID, userID, &status, nil, isAdmin)
}

// DeleteTicket removes a ticket (admin only)
func (s *TicketService) DeleteTicket(ctx context.Context, ticketID string) error {
	// First delete messages
	_, err := s.DB.ExecContext(ctx, "DELETE FROM ticket_messages WHERE ticket_id = $1", ticketID)
	if err != nil {
		return err
	}

	// Then delete ticket
	result, err := s.DB.ExecContext(ctx, "DELETE FROM tickets WHERE id = $1", ticketID)
	if err != nil {
		return err
	}

	rowsAffected, _ := result.RowsAffected()
	if rowsAffected == 0 {
		return errors.New("ticket not found")
	}

	return nil
}

// ListAllTickets returns all tickets (for admin)
func (s *TicketService) ListAllTickets(ctx context.Context, status string) ([]models.Ticket, error) {
	query := `
		SELECT id, user_id, subject, status, priority, created_at, updated_at
		FROM tickets
	`
	args := []interface{}{}

	if status != "" {
		query += " WHERE status = $1"
		args = append(args, status)
	}

	query += " ORDER BY updated_at DESC"

	rows, err := s.DB.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	tickets := []models.Ticket{}
	for rows.Next() {
		var t models.Ticket
		if err := rows.Scan(
			&t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt,
		); err != nil {
			return nil, err
		}
		tickets = append(tickets, t)
	}
	return tickets, nil
}

// Helper function
func joinStrings(strs []string, sep string) string {
	if len(strs) == 0 {
		return ""
	}
	result := strs[0]
	for i := 1; i < len(strs); i++ {
		result += sep + strs[i]
	}
	return result
}
