package httpapi

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"productier/apps/backend/internal/mailruntime"
	"productier/apps/backend/internal/store"
)

type connectMailboxRequest struct {
	WorkspaceSlug string `json:"workspaceSlug" binding:"required"`
	Label         string `json:"label"`
	Email         string `json:"email" binding:"required,email"`
	DisplayName   string `json:"displayName"`
	IMAPHost      string `json:"imapHost" binding:"required"`
	IMAPPort      int    `json:"imapPort"`
	IMAPUsername  string `json:"imapUsername"`
	IMAPPassword  string `json:"imapPassword" binding:"required"`
	IMAPUseTLS    bool   `json:"imapUseTls"`
	SMTPHost      string `json:"smtpHost" binding:"required"`
	SMTPPort      int    `json:"smtpPort"`
	SMTPUsername  string `json:"smtpUsername"`
	SMTPPassword  string `json:"smtpPassword"`
	SMTPUseTLS    bool   `json:"smtpUseTls"`
}

type createOutgoingMailRequest struct {
	WorkspaceSlug string              `json:"workspaceSlug" binding:"required"`
	MailboxID     string              `json:"mailboxId" binding:"required"`
	To            []store.MailAddress `json:"to" binding:"required"`
	Cc            []store.MailAddress `json:"cc"`
	Bcc           []store.MailAddress `json:"bcc"`
	Subject       string              `json:"subject"`
	TextBody      string              `json:"textBody"`
	HTMLBody      string              `json:"htmlBody"`
	ScheduledFor  *time.Time          `json:"scheduledFor"`
}

type createTaskFromMailRequest struct {
	BoardGroupID string     `json:"boardGroupId" binding:"required"`
	Title        string     `json:"title"`
	DueAt        *time.Time `json:"dueAt"`
	Color        string     `json:"color"`
}

func (s *Server) registerMailRoutes(group *gin.RouterGroup) {
	group.GET("/mailboxes", func(c *gin.Context) {
		workspaceSlug := c.Query("workspaceSlug")
		if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
			return
		}
		c.JSON(http.StatusOK, gin.H{"data": s.store.ListMailboxes(workspaceSlug)})
	})

	group.POST("/mailboxes", func(c *gin.Context) {
		var input connectMailboxRequest
		if err := c.ShouldBindJSON(&input); err != nil {
			s.writeStatusError(c, http.StatusBadRequest, err.Error())
			return
		}
		if _, ok := s.requireWorkspaceMember(c, input.WorkspaceSlug); !ok {
			return
		}

		mailbox, err := s.mail.ConnectMailbox(c.Request.Context(), mailruntime.ConnectMailboxInput{
			WorkspaceSlug: input.WorkspaceSlug,
			Label:         input.Label,
			Email:         input.Email,
			DisplayName:   input.DisplayName,
			IMAPHost:      input.IMAPHost,
			IMAPPort:      input.IMAPPort,
			IMAPUsername:  input.IMAPUsername,
			IMAPPassword:  input.IMAPPassword,
			IMAPUseTLS:    input.IMAPUseTLS,
			SMTPHost:      input.SMTPHost,
			SMTPPort:      input.SMTPPort,
			SMTPUsername:  input.SMTPUsername,
			SMTPPassword:  input.SMTPPassword,
			SMTPUseTLS:    input.SMTPUseTLS,
		})
		if err != nil {
			s.writeStatusError(c, http.StatusBadRequest, err.Error())
			return
		}
		c.JSON(http.StatusCreated, gin.H{"data": mailbox})
	})

	group.POST("/mailboxes/:mailboxId/sync", func(c *gin.Context) {
		mailbox, err := s.store.GetMailboxByID(c.Param("mailboxId"))
		if err != nil {
			s.writeStatusError(c, http.StatusNotFound, err.Error())
			return
		}
		if _, ok := s.requireWorkspaceMember(c, mailbox.WorkspaceSlug); !ok {
			return
		}
		if err := s.mail.SyncMailbox(c.Request.Context(), mailbox.ID); err != nil {
			s.writeStatusError(c, http.StatusBadGateway, err.Error())
			return
		}
		updated, err := s.store.GetMailboxByID(mailbox.ID)
		if err != nil {
			s.writeStatusError(c, http.StatusNotFound, err.Error())
			return
		}
		c.JSON(http.StatusOK, gin.H{"data": updated})
	})

	group.GET("/mail/messages", func(c *gin.Context) {
		workspaceSlug := c.Query("workspaceSlug")
		if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
			return
		}
		c.JSON(http.StatusOK, gin.H{"data": s.store.ListMailMessages(workspaceSlug, c.Query("mailboxId"))})
	})

	group.GET("/mail/outgoing", func(c *gin.Context) {
		workspaceSlug := c.Query("workspaceSlug")
		if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
			return
		}
		c.JSON(http.StatusOK, gin.H{"data": s.store.ListOutgoingMails(workspaceSlug, c.Query("mailboxId"))})
	})

	group.POST("/mail/outgoing", func(c *gin.Context) {
		var input createOutgoingMailRequest
		if err := c.ShouldBindJSON(&input); err != nil {
			s.writeStatusError(c, http.StatusBadRequest, err.Error())
			return
		}
		if _, ok := s.requireWorkspaceMember(c, input.WorkspaceSlug); !ok {
			return
		}
		mailbox, err := s.store.GetMailboxByID(input.MailboxID)
		if err != nil {
			s.writeStatusError(c, http.StatusNotFound, err.Error())
			return
		}
		if mailbox.WorkspaceSlug != input.WorkspaceSlug {
			s.writeStatusError(c, http.StatusForbidden, "mailbox does not belong to workspace")
			return
		}

		item, err := s.mail.QueueOutgoingMail(c.Request.Context(), mailruntime.QueueOutgoingMailInput{
			WorkspaceSlug: input.WorkspaceSlug,
			MailboxID:     input.MailboxID,
			To:            input.To,
			Cc:            input.Cc,
			Bcc:           input.Bcc,
			Subject:       input.Subject,
			TextBody:      input.TextBody,
			HTMLBody:      input.HTMLBody,
			ScheduledFor:  input.ScheduledFor,
		})
		if err != nil {
			s.writeStatusError(c, http.StatusBadGateway, err.Error())
			return
		}
		c.JSON(http.StatusCreated, gin.H{"data": item})
	})

	group.POST("/mail/messages/:messageId/create-task", func(c *gin.Context) {
		message, err := s.store.GetMailMessageByID(c.Param("messageId"))
		if err != nil {
			s.writeStatusError(c, http.StatusNotFound, err.Error())
			return
		}
		if _, ok := s.requireWorkspaceMember(c, message.WorkspaceSlug); !ok {
			return
		}
		if message.LinkedTaskID != nil {
			s.writeStatusError(c, http.StatusConflict, "message already linked to a task")
			return
		}

		var input createTaskFromMailRequest
		if err := c.ShouldBindJSON(&input); err != nil {
			s.writeStatusError(c, http.StatusBadRequest, err.Error())
			return
		}

		description := mailTaskDescription(message)
		task := s.store.CreateTask(store.CreateTaskInput{
			WorkspaceSlug: message.WorkspaceSlug,
			BoardGroupID:  input.BoardGroupID,
			Title:         firstNonBlank(strings.TrimSpace(input.Title), strings.TrimSpace(message.Subject), "Follow up on email"),
			Description:   description,
			DueAt:         input.DueAt,
			Color:         withFallback(input.Color, "blue"),
		})

		if _, err := s.store.LinkMailMessageTask(message.ID, task.ID); err != nil {
			s.writeStatusError(c, http.StatusInternalServerError, err.Error())
			return
		}
		c.JSON(http.StatusCreated, gin.H{"data": task})
	})
}

func mailTaskDescription(message store.MailMessage) string {
	var builder strings.Builder
	if message.From.Email != "" {
		builder.WriteString(fmt.Sprintf("From: %s <%s>\n\n", firstNonBlank(message.From.Name, "Sender"), message.From.Email))
	}
	body := firstNonBlank(strings.TrimSpace(message.TextBody), strings.TrimSpace(message.Snippet))
	builder.WriteString(body)
	return strings.TrimSpace(builder.String())
}

func firstNonBlank(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return value
		}
	}
	return ""
}

func withFallback(value string, fallback string) string {
	if strings.TrimSpace(value) == "" {
		return fallback
	}
	return value
}
