package mailer

import (
	"bytes"
	"context"
	"fmt"
	"time"

	"github.com/aarondl/opt/omit"
	"github.com/google/uuid"
	"github.com/rs/zerolog/log"
	"source.gleipnir.technology/Gleipnir/nidus-sync/config"
	"source.gleipnir.technology/Gleipnir/nidus-sync/db"
	"source.gleipnir.technology/Gleipnir/nidus-sync/db/models"
	"source.gleipnir.technology/Gleipnir/nidus-sync/lint"
	"source.gleipnir.technology/Gleipnir/nidus-sync/lob"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/file"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/pdf"
)

// This is the entrypoint for the backend job of sending a compliance mailer
// It should only return errors for programmer-level errors we want retried on restart
// Not for normal issues
func ComplianceSend(ctx context.Context, row_id int32) error {
	bxn := db.PGInstance.BobDB
	compliance_req, err := models.FindComplianceReportRequest(ctx, bxn, row_id)
	if err != nil {
		return fmt.Errorf("find compliance report: %w", err)
	}
	log.Debug().Int32("id", row_id).Str("public_id", compliance_req.PublicID).Msg("working on mailer")

	if compliance_req.LeadID.IsNull() {
		return fmt.Errorf("no lead for compliance req %d", compliance_req.ID)
	}
	lead_id := compliance_req.LeadID.MustGet()
	lead, err := models.FindLead(ctx, bxn, lead_id)
	if err != nil {
		return fmt.Errorf("find lead: %w", err)
	}

	if lead.SiteID.IsNull() {
		return fmt.Errorf("no site for lead %d", lead.ID)
	}
	site_id := lead.SiteID.MustGet()
	site, err := models.FindSite(ctx, bxn, site_id)
	if err != nil {
		return fmt.Errorf("find site: %w", err)
	}

	address, err := models.FindAddress(ctx, bxn, site.AddressID)
	if err != nil {
		return fmt.Errorf("find address: %w", err)
	}
	if address.PostalCode == "" {
		log.Warn().Int32("id", address.ID).Msg("dropping mailer job because the address has no postal code")
		return nil
	}

	organization, err := models.FindOrganization(ctx, bxn, site.OrganizationID)
	if err != nil {
		return fmt.Errorf("find address: %w", err)
	}
	if organization.LobAddressID.IsNull() {
		return fmt.Errorf("organization %d has no Lob Address ID", organization.ID)
	}

	path := fmt.Sprintf("/mailer/mode-3/%s/preview", compliance_req.PublicID)
	content, err := pdf.GeneratePDF(ctx, path)
	if err != nil {
		return fmt.Errorf("generate pdf: %w", err)
	}
	err = file.MailerFromReader(compliance_req.PublicID, bytes.NewReader(content))
	if err != nil {
		return fmt.Errorf("save pdf: %w", err)
	}

	// Do the part where we actually send to the mailer service
	if organization.LobAddressID.IsNull() {
		return fmt.Errorf("lob address for %d is null", organization.ID)
	}
	lob_address := organization.LobAddressID.MustGet()
	letter, err := sendMail(ctx, lob_address, compliance_req.PublicID, site, address, content)
	if err != nil {
		return fmt.Errorf("send mail: %w", err)
	}

	mailer_uuid, err := uuid.NewUUID()
	if err != nil {
		return fmt.Errorf("generate uuid: %w", err)
	}
	txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("start txn: %w", err)
	}
	defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback")
	mailer, err := models.CommsMailers.Insert(&models.CommsMailerSetter{
		AddressID:  omit.From(address.ID),
		Created:    omit.From(time.Now()),
		ExternalID: omit.From(letter.ID),
		// ID
		Recipient: omit.From(site.OwnerName),
		UUID:      omit.From(mailer_uuid),
	}).One(ctx, txn)
	if err != nil {
		return fmt.Errorf("create comms mailer: %w", err)
	}

	crrm, err := models.ComplianceReportRequestMailers.Insert(&models.ComplianceReportRequestMailerSetter{
		ComplianceReportRequestID: omit.From(compliance_req.ID),
		// ID
		MailerID: omit.From(mailer.ID),
	}).One(ctx, txn)
	if err != nil {
		return fmt.Errorf("create crrm: %w", err)
	}
	log.Info().Int32("id", crrm.ID).Msg("Created compliance report request mailer")
	lint.LogOnErrCtx(txn.Commit, ctx, "commit")
	return nil
}

func sendMail(ctx context.Context, org_address_id string, public_id string, site *models.Site, address *models.Address, content []byte) (*lob.Letter, error) {
	key := config.LobAPIKey
	client := lob.NewLob(key)
	line1 := address.Number + " " + address.Street
	addr_req := lob.RequestAddressCreate{
		AddressLine1: line1,
		AddressCity:  address.Locality,
		AddressState: address.Region,
		AddressZip:   address.PostalCode,
		Name:         site.OwnerName,
	}
	addr_to, err := client.AddressCreate(ctx, addr_req)
	if err != nil {
		return nil, fmt.Errorf("create to addr for address %d: %w", address.ID, err)
	}

	req := lob.RequestLetterCreate{
		To:      addr_to.ID,
		From:    org_address_id,
		File:    bytes.NewReader(content),
		Color:   true,
		UseType: "operational",
	}
	letter, err := client.LetterCreate(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("letter create for address %d: %w", address.ID, err)
	}

	return &letter, nil
}
