package controller

import (
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/oapi-codegen/nullable"
	"github.com/phishingclub/phishingclub/database"
	"github.com/phishingclub/phishingclub/errs"
	"github.com/phishingclub/phishingclub/model"
	"github.com/phishingclub/phishingclub/repository"
	"github.com/phishingclub/phishingclub/service"
	"github.com/phishingclub/phishingclub/vo"
)

// EmailOrderByMap is a map between the frontend and the backend
// so the frontend has user friendly names instead of direct references
// to the database schema
// this is tied to a slice in the repository package
var EmailOrderByMap = map[string]string{
	"created_at":     repository.TableColumn(database.EMAIL_TABLE, "created_at"),
	"updated_at":     repository.TableColumn(database.EMAIL_TABLE, "created_at"),
	"name":           repository.TableColumn(database.EMAIL_TABLE, "name"),
	"mail_from":      repository.TableColumn(database.EMAIL_TABLE, "mail_from"),
	"from":           repository.TableColumn(database.EMAIL_TABLE, "from"),
	"subject":        repository.TableColumn(database.EMAIL_TABLE, "subject"),
	"tracking_pixel": repository.TableColumn(database.EMAIL_TABLE, "add_tracking_pixel"),
}

// AddAttachmentsToEmailRequest is a request to add attachments to a message
// supports both old format (ids array) and new format (attachments array with isInline)
type AddAttachmentsToEmailRequest struct {
	IDs         []string               `json:"ids"`         // deprecated: old format for backward compatibility
	Attachments []AttachmentWithInline `json:"attachments"` // new format with isInline support
}

// AttachmentWithInline represents an attachment ID with inline flag
type AttachmentWithInline struct {
	ID       string `json:"id"`
	IsInline bool   `json:"isInline"`
}

// RemoveAttachmentFromEmailRequest is a request to remove an attachment from a message
type RemoveAttachmentFromEmailRequest struct {
	AttachmentID string `json:"attachmentID"`
}

// SendTestEmailRequest is a request for sending a test of an e-mail
type SendTestEmailRequest struct {
	SMTPID      *uuid.UUID
	DomainID    *uuid.UUID
	RecipientID *uuid.UUID
}

// Email is a Email controller
type Email struct {
	Common
	EmailService    *service.Email
	TemplateService *service.Template
	EmailRepository *repository.Email
}

// AddAttachments adds attachments to a email
func (m *Email) AddAttachments(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	var request AddAttachmentsToEmailRequest
	if ok := m.handleParseRequest(g, &request); !ok {
		return
	}
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}

	// handle backward compatibility: if old format (ids) is used, convert to new format
	if len(request.IDs) > 0 && len(request.Attachments) == 0 {
		for _, idStr := range request.IDs {
			request.Attachments = append(request.Attachments, AttachmentWithInline{
				ID:       idStr,
				IsInline: false, // default to false for backward compatibility
			})
		}
	}

	if len(request.Attachments) == 0 {
		m.Response.BadRequestMessage(g, "No attachments provided")
		return
	}
	attachments := []service.AttachmentWithInline{}
	for _, att := range request.Attachments {
		id, err := uuid.Parse(att.ID)
		if err != nil {
			m.Logger.Debugw(errs.MsgFailedToParseUUID,
				"error", err,
			)
			m.Response.BadRequestMessage(g, "Invalid attachment ID")
			return
		}
		attachments = append(attachments, service.AttachmentWithInline{
			ID:       &id,
			IsInline: att.IsInline,
		})
	}
	// add attachments to email
	err := m.EmailService.AddAttachments(
		g.Request.Context(),
		session,
		id,
		attachments,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{})
}

// RemoveAttachment removes an attachment from a email
func (m *Email) RemoveAttachment(g *gin.Context) {
	// handle session
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse req
	var req RemoveAttachmentFromEmailRequest
	if ok := m.handleParseRequest(g, &req); !ok {
		return
	}
	attachmentID, err := uuid.Parse(req.AttachmentID)
	if err != nil {
		m.Logger.Debugw(errs.MsgFailedToParseUUID,
			"error", err,
		)
		m.Response.BadRequestMessage(g, "Invalid attachment ID")
		return
	}
	emailID, err := uuid.Parse(g.Param("id"))
	if err != nil {
		m.Logger.Debugw(errs.MsgFailedToParseUUID, "error", err)
		m.Response.BadRequestMessage(g, "Invalid message ID")
		return
	}
	// remove attachment from email
	err = m.EmailService.RemoveAttachment(
		g.Request.Context(),
		session,
		&emailID,
		&attachmentID,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{})
}

// Create creates a email
func (m *Email) Create(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse req
	var req model.Email
	if ok := m.handleParseRequest(g, &req); !ok {
		return
	}
	// save email
	id, err := m.EmailService.Create(
		g,
		session,
		&req,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{
		"id": id,
	})
}

// SendTestEmail
func (m *Email) SendTestEmail(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}
	var req SendTestEmailRequest
	if ok := m.handleParseRequest(g, &req); !ok {
		return
	}
	companyID := companyIDFromRequestQuery(g)
	// send test email
	err := m.EmailService.SendTestEmail(
		g,
		session,
		id,
		req.SMTPID,
		req.DomainID,
		req.RecipientID,
		companyID,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{})
}

// GetByID gets a email by ID
func (m *Email) GetByID(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}
	companyID := companyIDFromRequestQuery(g)
	// get email
	email, err := m.EmailService.GetByID(
		g.Request.Context(),
		session,
		id,
		companyID,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, email)
}

// GetContentByID gets a email content by ID
func (m *Email) GetContentByID(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}
	companyID := companyIDFromRequestQuery(g)
	// get
	email, err := m.EmailService.GetByID(
		g.Request.Context(),
		session,
		id,
		companyID,
	)
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	// build email
	domain := &model.Domain{
		Name: nullable.NewNullableWithValue(
			*vo.NewString255Must("example.test"),
		),
	}
	recipient := model.NewRecipientExample()
	campaignRecipient := model.CampaignRecipient{
		ID: nullable.NewNullableWithValue(
			uuid.New(),
		),
		Recipient: recipient,
	}
	apiSender := model.NewAPISenderExample()
	emailBody, err := m.TemplateService.CreateMailBody(
		g.Request.Context(),
		"id",
		"/foo",
		domain,
		&campaignRecipient,
		email,
		apiSender,
		companyID, // use the company context from the email
	)
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, emailBody)
}

// GetAll gets all emails using pagination
func (m *Email) GetAll(g *gin.Context) {
	// handle session
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	companyID := companyIDFromRequestQuery(g)
	queryArgs, ok := m.handleQueryArgs(g)
	if !ok {
		return
	}
	queryArgs.DefaultSortByName()
	queryArgs.RemapOrderBy(EmailOrderByMap)
	emails, err := m.EmailService.GetAll(
		g.Request.Context(),
		session,
		companyID,
		queryArgs,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return

	}
	m.Response.OK(g, emails)
}

// GetOverviews gets all email overviews using pagination
func (m *Email) GetOverviews(g *gin.Context) {
	// handle session
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	companyID := companyIDFromRequestQuery(g)
	queryArgs, ok := m.handleQueryArgs(g)
	if !ok {
		return
	}
	queryArgs.RemapOrderBy(EmailOrderByMap)
	queryArgs.DefaultSortByName()
	emails, err := m.EmailService.GetOverviews(
		g.Request.Context(),
		session,
		companyID,
		queryArgs,
	)
	// handle responses
	if ok := m.handleErrors(g, err); !ok {
		return

	}
	m.Response.OK(g, emails)
}

// UpdateByID updates a message by ID
func (m *Email) UpdateByID(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}
	var email model.Email
	if ok := m.handleParseRequest(g, &email); !ok {
		return
	}
	// update message
	err := m.EmailService.UpdateByID(
		g.Request.Context(),
		session,
		id,
		&email,
	)
	// handle response
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{})
}

// DeleteByID deletes a message by ID
func (m *Email) DeleteByID(g *gin.Context) {
	session, _, ok := m.handleSession(g)
	if !ok {
		return
	}
	// parse request
	id, ok := m.handleParseIDParam(g)
	if !ok {
		return
	}
	// delete message
	err := m.EmailService.DeleteByID(
		g.Request.Context(),
		session,
		id,
	)
	// handle response
	if ok := m.handleErrors(g, err); !ok {
		return
	}
	m.Response.OK(g, gin.H{})
}
