package tickets

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"strings"
	"time"

	"github.com/TaruLab/keluargamart-backend/internal/auth"
	"github.com/TaruLab/keluargamart-backend/internal/mailer"
)

var (
	ErrTitleRequired       = errors.New("judul tiket wajib diisi")
	ErrDescriptionRequired = errors.New("deskripsi tiket wajib diisi")
	ErrInvalidCategory     = errors.New("kategori tiket tidak valid")
	ErrInvalidPriority     = errors.New("prioritas tiket tidak valid")
	ErrInvalidStatus       = errors.New("status tiket tidak valid")
	ErrCommentRequired     = errors.New("komentar tiket wajib diisi")
	ErrUnauthorized        = errors.New("tidak memiliki hak akses tiket ini")
)

var validCategories = map[string]bool{
	"bug": true, "pos": true, "b2b": true, "erp": true, "inventory": true,
	"hardware": true, "billing": true, "feature": true, "other": true,
}

var validPriorities = map[string]bool{
	"low": true, "medium": true, "high": true, "critical": true,
}

var validStatuses = map[string]bool{
	"open": true, "triaged": true, "in_progress": true,
	"waiting_reporter": true, "resolved": true, "closed": true,
}

var validSourceApps = map[string]bool{
	"erp": true, "pos": true, "b2b": true, "nextreport": true, "other": true,
}

type repository interface {
	CreateTicket(ctx context.Context, t *Ticket) (*Ticket, error)
	GetTicketByID(ctx context.Context, id int64) (*Ticket, error)
	ListTickets(ctx context.Context, filter ListTicketsFilter) ([]Ticket, int64, error)
	UpdateTicket(ctx context.Context, id int64, req UpdateTicketRequest) (*Ticket, error)
	CreateComment(ctx context.Context, c *TicketComment) (*TicketComment, error)
	ListCommentsByTicketID(ctx context.Context, ticketID int64, includeInternal bool) ([]TicketComment, error)
}

type Service struct {
	repo         repository
	mailer       mailer.Mailer
	supportEmail string
	asyncEmail   bool
}

func NewService(repo repository) *Service {
	return &Service{
		repo:       repo,
		asyncEmail: true,
	}
}

// WithMailer wires the outbound email sender and central support inbox address.
// If mailer is nil, ticket emails are skipped.
func (s *Service) WithMailer(m mailer.Mailer, supportEmail string) *Service {
	s.mailer = m
	s.supportEmail = strings.TrimSpace(supportEmail)
	return s
}

// WithSyncEmail configures email delivery to run synchronously (useful in tests).
func (s *Service) WithSyncEmail() *Service {
	s.asyncEmail = false
	return s
}

func (s *Service) sendEmail(to, subject, body string) {
	if s.mailer == nil || strings.TrimSpace(to) == "" {
		return
	}
	to = strings.TrimSpace(to)
	if s.asyncEmail {
		go func() {
			ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
			defer cancel()
			if err := s.mailer.Send(ctx, to, subject, body); err != nil {
				slog.Error("failed to send ticket email", "to", to, "subject", subject, "error", err)
			} else {
				slog.Info("ticket email sent successfully", "to", to, "subject", subject)
			}
		}()
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := s.mailer.Send(ctx, to, subject, body); err != nil {
		slog.Error("failed to send ticket email synchronously", "to", to, "subject", subject, "error", err)
	} else {
		slog.Info("ticket email sent successfully", "to", to, "subject", subject)
	}
}

func (s *Service) Create(ctx context.Context, user *auth.User, req CreateTicketRequest) (*Ticket, error) {
	if user == nil {
		return nil, ErrUnauthorized
	}
	title := strings.TrimSpace(req.Title)
	if title == "" {
		return nil, ErrTitleRequired
	}
	desc := strings.TrimSpace(req.Description)
	if desc == "" {
		return nil, ErrDescriptionRequired
	}

	cat := strings.ToLower(strings.TrimSpace(req.Category))
	if cat == "" {
		cat = "other"
	} else if !validCategories[cat] {
		switch cat {
		case "pemesanan & faktur", "billing", "pembayaran", "faktur", "akun & pembayaran":
			cat = "billing"
		case "pengiriman & armada", "pengiriman", "armada", "layanan depot galon", "galon", "order":
			cat = "b2b"
		case "kendala teknis / website", "teknis", "website", "bug sistem":
			cat = "bug"
		default:
			return nil, ErrInvalidCategory
		}
	}

	pri := strings.ToLower(strings.TrimSpace(req.Priority))
	if pri == "" {
		pri = "medium"
	} else if !validPriorities[pri] {
		return nil, ErrInvalidPriority
	}

	src := strings.ToLower(strings.TrimSpace(req.SourceApp))
	if src == "" || !validSourceApps[src] {
		src = "erp"
	}

	meta := req.Metadata
	if meta == nil {
		meta = map[string]any{}
	}

	t := &Ticket{
		Title:         title,
		Description:   desc,
		Category:      cat,
		Priority:      pri,
		Status:        "open",
		SourceApp:     src,
		SourcePath:    req.SourcePath,
		ReporterID:    user.ID,
		ReporterName:  user.Name,
		ReporterEmail: user.Email,
		Metadata:      meta,
	}

	created, err := s.repo.CreateTicket(ctx, t)
	if err != nil {
		return nil, err
	}

	if s.mailer != nil {
		createdAtStr := created.CreatedAt.In(time.FixedZone("WIB", 7*3600)).Format("02 Jan 2006 15:04 WIB")
		if created.ReporterEmail != "" {
			userSubject := fmt.Sprintf("[KeluargaMart Support] Tiket Baru #%s: %s", created.TicketNumber, created.Title)
			userBody := fmt.Sprintf(
				"Halo %s,\n\n"+
					"Tiket bantuan Anda telah berhasil dibuat dengan rincian berikut:\n\n"+
					"Nomor Tiket : %s\n"+
					"Judul       : %s\n"+
					"Kategori    : %s\n"+
					"Prioritas   : %s\n"+
					"Aplikasi    : %s\n"+
					"Status      : %s\n"+
					"Waktu       : %s\n\n"+
					"Deskripsi:\n%s\n\n"+
					"---\n"+
					"Tim KeluargaMart Support akan segera meninjau dan menindaklanjuti laporan Anda.",
				created.ReporterName, created.TicketNumber, created.Title, created.Category, created.Priority, created.SourceApp, strings.ToUpper(created.Status), createdAtStr, created.Description,
			)
			s.sendEmail(created.ReporterEmail, userSubject, userBody)
		}

		if s.supportEmail != "" && s.supportEmail != created.ReporterEmail {
			supportSubject := fmt.Sprintf("[KeluargaMart Support] Tiket Baru #%s: %s", created.TicketNumber, created.Title)
			supportBody := fmt.Sprintf(
				"Tiket bantuan baru telah dibuat di sistem:\n\n"+
					"Nomor Tiket : %s\n"+
					"Pelapor     : %s (%s)\n"+
					"Judul       : %s\n"+
					"Kategori    : %s\n"+
					"Prioritas   : %s\n"+
					"Aplikasi    : %s (%s)\n"+
					"Status      : %s\n"+
					"Waktu       : %s\n\n"+
					"Deskripsi:\n%s\n\n"+
					"---\n"+
					"Silakan tanggapi tiket ini melalui portal ERP KeluargaMart.",
				created.TicketNumber, created.ReporterName, created.ReporterEmail, created.Title, created.Category, created.Priority, created.SourceApp, created.SourcePath, strings.ToUpper(created.Status), createdAtStr, created.Description,
			)
			s.sendEmail(s.supportEmail, supportSubject, supportBody)
		}
	}

	return created, nil
}

func (s *Service) Get(ctx context.Context, user *auth.User, id int64) (*Ticket, []TicketComment, error) {
	if user == nil {
		return nil, nil, ErrUnauthorized
	}
	ticket, err := s.repo.GetTicketByID(ctx, id)
	if err != nil {
		return nil, nil, err
	}

	canManage := user.Can("support.tickets.manage")
	isBuyer := user.Role == "buyer" || user.Role == "customer"
	if (!canManage && !user.Can("support.tickets.view") && ticket.ReporterID != user.ID) || (isBuyer && ticket.ReporterID != user.ID) {
		return nil, nil, ErrUnauthorized
	}

	comments, err := s.repo.ListCommentsByTicketID(ctx, id, canManage)
	if err != nil {
		return nil, nil, err
	}

	return ticket, comments, nil
}

func (s *Service) List(ctx context.Context, user *auth.User, filter ListTicketsFilter) ([]Ticket, int64, error) {
	if user == nil {
		return nil, 0, ErrUnauthorized
	}
	canManage := user.Can("support.tickets.manage")
	canViewAll := user.Can("support.tickets.view")
	isBuyer := user.Role == "buyer" || user.Role == "customer"

	if isBuyer || (!canManage && !canViewAll) {
		filter.ReporterID = user.ID
	}

	return s.repo.ListTickets(ctx, filter)
}

func (s *Service) Update(ctx context.Context, user *auth.User, id int64, req UpdateTicketRequest) (*Ticket, error) {
	if user == nil || !user.Can("support.tickets.manage") {
		return nil, ErrUnauthorized
	}

	if req.Status != nil {
		status := strings.ToLower(strings.TrimSpace(*req.Status))
		if !validStatuses[status] {
			return nil, ErrInvalidStatus
		}
		req.Status = &status
	}

	if req.Priority != nil {
		priority := strings.ToLower(strings.TrimSpace(*req.Priority))
		if !validPriorities[priority] {
			return nil, ErrInvalidPriority
		}
		req.Priority = &priority
	}

	updated, err := s.repo.UpdateTicket(ctx, id, req)
	if err != nil {
		return nil, err
	}

	if s.mailer != nil && req.Status != nil && updated.ReporterEmail != "" {
		subject := fmt.Sprintf("[KeluargaMart Support] Status Tiket #%s Diperbarui: %s", updated.TicketNumber, strings.ToUpper(*req.Status))
		body := fmt.Sprintf(
			"Halo %s,\n\n"+
				"Status tiket bantuan Anda #%s (%s) telah diperbarui menjadi: %s.\n\n"+
				"---\n"+
				"Terima kasih telah menggunakan layanan KeluargaMart.",
			updated.ReporterName, updated.TicketNumber, updated.Title, strings.ToUpper(*req.Status),
		)
		s.sendEmail(updated.ReporterEmail, subject, body)
	}

	return updated, nil
}

func (s *Service) AddComment(ctx context.Context, user *auth.User, ticketID int64, req CreateCommentRequest) (*TicketComment, error) {
	if user == nil {
		return nil, ErrUnauthorized
	}
	commentText := strings.TrimSpace(req.Comment)
	if commentText == "" {
		return nil, ErrCommentRequired
	}

	ticket, err := s.repo.GetTicketByID(ctx, ticketID)
	if err != nil {
		return nil, err
	}

	canManage := user.Can("support.tickets.manage")
	if !canManage && ticket.ReporterID != user.ID {
		return nil, ErrUnauthorized
	}

	isInternal := req.IsInternal && canManage

	c := &TicketComment{
		TicketID:   ticketID,
		UserID:     user.ID,
		UserName:   user.Name,
		Comment:    commentText,
		IsInternal: isInternal,
	}

	comment, err := s.repo.CreateComment(ctx, c)
	if err != nil {
		return nil, err
	}

	if s.mailer != nil && !comment.IsInternal {
		createdAtStr := comment.CreatedAt.In(time.FixedZone("WIB", 7*3600)).Format("02 Jan 2006 15:04 WIB")
		commentSubject := fmt.Sprintf("[KeluargaMart Support] Komentar Baru pada Tiket #%s: %s", ticket.TicketNumber, ticket.Title)

		if comment.UserID == ticket.ReporterID {
			// Reporter commented -> notify support inbox
			if s.supportEmail != "" && s.supportEmail != ticket.ReporterEmail {
				supportBody := fmt.Sprintf(
					"Pelapor menambahkan komentar baru pada tiket #%s (%s):\n\n"+
						"Pelapor  : %s\n"+
						"Kategori : %s\n"+
						"Waktu    : %s\n\n"+
						"Komentar:\n%s\n\n"+
						"---\n"+
						"Silakan tanggapi komentar ini melalui portal ERP KeluargaMart.",
					ticket.TicketNumber, ticket.Title, comment.UserName, ticket.Category, createdAtStr, comment.Comment,
				)
				s.sendEmail(s.supportEmail, commentSubject, supportBody)
			}
		} else {
			// Staff commented -> notify reporter
			if ticket.ReporterEmail != "" {
				userBody := fmt.Sprintf(
					"Halo %s,\n\n"+
						"Tim KeluargaMart Support telah menambahkan balasan pada tiket Anda #%s (%s):\n\n"+
						"Petugas : %s\n"+
						"Waktu   : %s\n\n"+
						"Balasan / Komentar:\n%s\n\n"+
						"---\n"+
						"Anda dapat memantau status atau membalas tiket ini melalui portal KeluargaMart.",
					ticket.ReporterName, ticket.TicketNumber, ticket.Title, comment.UserName, createdAtStr, comment.Comment,
				)
				s.sendEmail(ticket.ReporterEmail, commentSubject, userBody)
			}
		}
	}

	return comment, nil
}
