package postgres

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"time"

	"github.com/rede5/gohorsejobs/backend/internal/core/domain/entity"
)

type EmailRepository struct {
	db *sql.DB
}

func NewEmailRepository(db *sql.DB) *EmailRepository {
	return &EmailRepository{db: db}
}

// Templates

func (r *EmailRepository) ListTemplates(ctx context.Context) ([]*entity.EmailTemplate, error) {
	query := `SELECT id, slug, subject, body_html, variables, created_at, updated_at FROM email_templates ORDER BY slug`
	rows, err := r.db.QueryContext(ctx, query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var templates []*entity.EmailTemplate
	for rows.Next() {
		t := &entity.EmailTemplate{}
		var varsJSON []byte
		if err := rows.Scan(&t.ID, &t.Slug, &t.Subject, &t.BodyHTML, &varsJSON, &t.CreatedAt, &t.UpdatedAt); err != nil {
			return nil, err
		}
		if len(varsJSON) > 0 {
			json.Unmarshal(varsJSON, &t.Variables)
		}
		templates = append(templates, t)
	}
	return templates, nil
}

func (r *EmailRepository) GetTemplate(ctx context.Context, slug string) (*entity.EmailTemplate, error) {
	query := `SELECT id, slug, subject, body_html, variables, created_at, updated_at FROM email_templates WHERE slug = $1`
	row := r.db.QueryRowContext(ctx, query, slug)

	t := &entity.EmailTemplate{}
	var varsJSON []byte
	err := row.Scan(&t.ID, &t.Slug, &t.Subject, &t.BodyHTML, &varsJSON, &t.CreatedAt, &t.UpdatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	if len(varsJSON) > 0 {
		json.Unmarshal(varsJSON, &t.Variables)
	}
	return t, nil
}

func (r *EmailRepository) CreateTemplate(ctx context.Context, tmpl *entity.EmailTemplate) error {
	query := `INSERT INTO email_templates (slug, subject, body_html, variables, updated_at) VALUES ($1, $2, $3, $4, $5) RETURNING id, created_at`

	varsJSON, _ := json.Marshal(tmpl.Variables)
	if varsJSON == nil {
		varsJSON = []byte("[]")
	}

	return r.db.QueryRowContext(ctx, query, tmpl.Slug, tmpl.Subject, tmpl.BodyHTML, varsJSON, time.Now()).Scan(&tmpl.ID, &tmpl.CreatedAt)
}

func (r *EmailRepository) UpdateTemplate(ctx context.Context, tmpl *entity.EmailTemplate) error {
	query := `UPDATE email_templates SET subject=$1, body_html=$2, variables=$3, updated_at=$4 WHERE slug=$5`

	varsJSON, _ := json.Marshal(tmpl.Variables)
	if varsJSON == nil {
		varsJSON = []byte("[]")
	}

	res, err := r.db.ExecContext(ctx, query, tmpl.Subject, tmpl.BodyHTML, varsJSON, time.Now(), tmpl.Slug)
	if err != nil {
		return err
	}
	affected, _ := res.RowsAffected()
	if affected == 0 {
		return errors.New("template not found")
	}
	return nil
}

func (r *EmailRepository) DeleteTemplate(ctx context.Context, slug string) error {
	_, err := r.db.ExecContext(ctx, "DELETE FROM email_templates WHERE slug=$1", slug)
	return err
}

// Settings

func (r *EmailRepository) GetSettings(ctx context.Context) (*entity.EmailSettings, error) {
	// We assume there's mostly one active setting, order by updated_at desc
	query := `SELECT id, provider, smtp_host, smtp_port, smtp_user, smtp_pass, smtp_secure, sender_name, sender_email, amqp_url, is_active, updated_at 
	          FROM email_settings WHERE is_active = true ORDER BY updated_at DESC LIMIT 1`

	row := r.db.QueryRowContext(ctx, query)
	s := &entity.EmailSettings{}

	err := row.Scan(
		&s.ID, &s.Provider, &s.SMTPHost, &s.SMTPPort, &s.SMTPUser, &s.SMTPPass,
		&s.SMTPSecure, &s.SenderName, &s.SenderEmail, &s.AMQPURL, &s.IsActive, &s.UpdatedAt,
	)
	if err != nil {
		if err == sql.ErrNoRows {
			// Return default empty struct or nil
			return nil, nil
		}
		return nil, err
	}
	return s, nil
}

func (r *EmailRepository) UpdateSettings(ctx context.Context, s *entity.EmailSettings) error {
	// We can either update the existing row or Insert a new one (audit history).
	// Let's Insert a new one and set others to inactive if we want history, OR just update for simplicity as per requirement.
	// Migration 027 says "id UUID PRIMARY KEY". Let's try to update if ID exists, else insert.
	// Actually, GetSettings fetches the latest active.
	// Let's implement logical "Upsert active settings".

	// If ID is provided, update. If not, insert.
	if s.ID != "" {
		query := `UPDATE email_settings SET 
			provider=$1, smtp_host=$2, smtp_port=$3, smtp_user=$4, smtp_pass=$5, smtp_secure=$6, 
			sender_name=$7, sender_email=$8, amqp_url=$9, is_active=$10, updated_at=$11
			WHERE id=$12`
		_, err := r.db.ExecContext(ctx, query,
			s.Provider, s.SMTPHost, s.SMTPPort, s.SMTPUser, s.SMTPPass, s.SMTPSecure,
			s.SenderName, s.SenderEmail, s.AMQPURL, s.IsActive, time.Now(), s.ID,
		)
		return err
	}

	// Insert new
	query := `INSERT INTO email_settings (
		provider, smtp_host, smtp_port, smtp_user, smtp_pass, smtp_secure, 
		sender_name, sender_email, amqp_url, is_active)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id`

	return r.db.QueryRowContext(ctx, query,
		s.Provider, s.SMTPHost, s.SMTPPort, s.SMTPUser, s.SMTPPass, s.SMTPSecure,
		s.SenderName, s.SenderEmail, s.AMQPURL, true,
	).Scan(&s.ID)
}
