package mail

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/mail"
	"pocketbase/custom/cloudflare"
	"strings"
	"text/template"
	"time"

	"github.com/pocketbase/pocketbase"
	"github.com/pocketbase/pocketbase/core"
	"github.com/pocketbase/pocketbase/tools/mailer"
	"github.com/pocketbase/pocketbase/tools/router"
)

type EmailData struct {
	Name    string `json:"name"`
	Email   string `json:"email"`
	Token   string `json:"token"`
	Message string `json:"message"`
	Phone   string `json:"phone"`
	LangIso string `json:"lang_iso"`
}

type MailsSettings struct {
	ReceiverMail string `json:"receiver_mail"`
	ReceiverName string `json:"receiver_name"`
	Subject      string `json:"subject"`
}

func ServeMailSender(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
	return se.Router.POST("/api/email/send", func(e *core.RequestEvent) error {
		// name := e.Request.PathValue("name")

		data := EmailData{}
		if err := e.BindBody(&data); err != nil {
			e.Response.Header().Set("Content-Type", "application/json")
			app.Logger().Error(err.Error(), "type", "mail")
			return e.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
		}

		if pass, err := validateMX(data.Email); !pass && err != nil {
			e.Response.Header().Set("Content-Type", "application/json")
			app.Logger().Error("Invalid email address.", "type", "mail", "error", err.Error())
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid email address."})
		}

		// fmt.Printf("e.Request.Body: %v IP: %s\n", data, e.RealIP())

		err := cloudflare.VerifyTurnstile(app, data.Token, e.RealIP())
		if err != nil {
			e.Response.Header().Set("Content-Type", "application/json")
			app.Logger().Error("Captcha verification failed.", "type", "mail", "error", err.Error())
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Captcha verification failed."})
		}

		eamil_body, err := app.FindFirstRecordByFilter("email_template", fmt.Sprintf("name='contact_form'&&id_lang='%s'", data.LangIso), nil)
		if err != nil {
			e.Response.Header().Set("Content-Type", "application/json")
			app.Logger().Error("Template not available", "type", "mail", "error", err.Error())
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Template not available"})
		}
		templ, err := template.New("test").Parse(eamil_body.GetString("template"))
		if err != nil {
			app.Logger().Error("Template parsing error.", "type", "mail", "error", err.Error())
			e.Response.Header().Set("Content-Type", "application/json")
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Template parsing error."})
		}

		buf := bytes.Buffer{}
		templ.Execute(&buf, map[string]interface{}{
			"Data":    time.Now().Local().Format("2006-01-02 15:04:05"),
			"Name":    data.Name,
			"Email":   data.Email,
			"Message": strings.ReplaceAll(data.Message, "\n", "<br>"),
			"Phone":   data.Phone,
		})

		mailsSettings, err := getSettings(app)
		if err != nil {
			app.Logger().Error("Mails settings corrupted", "type", "mail", "error", err.Error())
			e.Response.Header().Set("Content-Type", "application/json")
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Mails settings corrupted"})
		}

		cc := []mail.Address{{Address: data.Email, Name: data.Name}}
		to := []mail.Address{{Address: mailsSettings.ReceiverMail, Name: mailsSettings.ReceiverName}}

		message := &mailer.Message{
			From: mail.Address{
				Address: e.App.Settings().Meta.SenderAddress,
				Name:    e.App.Settings().Meta.SenderName,
			},
			Cc:      cc,
			To:      to,
			Subject: mailsSettings.Subject,
			HTML:    buf.String(),
		}

		err = e.App.NewMailClient().Send(message)
		if err != nil {
			app.Logger().Error("Mails sending error", "type", "mail", "error", err.Error())
			e.Response.Header().Set("Content-Type", "application/json")
			return e.JSON(http.StatusBadRequest, map[string]string{"error": "Mails sending error"})
		}

		receivers := formReceiversList(to, cc, []mail.Address{})
		app.Logger().Info("Mail Sent", "type", "mail", "receivers", receivers)
		return e.JSON(http.StatusOK, map[string]string{"status": "success", "message": "Email sent successfully. to " + receivers})
	})
}

func formReceiversList(to []mail.Address, cc []mail.Address, bcc []mail.Address) string {

	res := ""

	for _, a := range to {
		res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
	}

	for _, a := range cc {
		res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
	}

	for _, a := range bcc {
		res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
	}
	return res
}
func getSettings(app *pocketbase.PocketBase) (*MailsSettings, error) {
	record, err := app.FindFirstRecordByFilter("settings", "key='contact_page'", nil)

	settings := MailsSettings{}
	json.Unmarshal([]byte(record.GetString("value")), &settings)

	if err != nil {
		return nil, err
	}

	return &settings, nil
}

func extractDomain(email string) string {
	parts := strings.Split(email, "@")
	if len(parts) != 2 {
		return ""
	}
	return strings.TrimSpace(parts[1])
}

// validateMX checks if the domain has valid MX records or A records as a fallback
func validateMX(email string) (bool, error) {
	// Check MX records

	domain := extractDomain(email)
	mxRecords, err := net.LookupMX(domain)
	if err != nil {
		return false, fmt.Errorf("'MX' records for %s not found", domain)
	} else if len(mxRecords) > 0 {
		// At least one MX record exists
		return true, nil
	}

	// Fallback: Check for A records (some domains accept mail via A records)
	aRecords, err := net.LookupHost(domain)
	if err != nil {
		return false, fmt.Errorf("'A' record for %s not found", domain)
	}
	return len(aRecords) > 0, nil
}
