package models

import (
	"fmt"
	"strings"

	"odoo-go/pkg/orm"
)

// initAccountFollowup registers payment follow-up models.
// Mirrors: odoo/addons/account_followup/models/account_followup.py
//
// Follow-up levels define escalation steps for overdue payments:
// Level 1: Friendly reminder after X days
// Level 2: Formal notice after X days
// Level 3: Final warning / legal action after X days
func initAccountFollowup() {
	// -- Follow-up Level --
	fl := orm.NewModel("account.followup.line", orm.ModelOpts{
		Description: "Follow-up Criteria",
		Order:       "delay",
	})

	fl.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Follow-Up Action", Required: true, Translate: true}),
		orm.Integer("delay", orm.FieldOpts{
			String:   "Due Days",
			Required: true,
			Help:     "Number of days after the due date of the invoice to trigger this action",
		}),
		orm.Text("description", orm.FieldOpts{
			String:    "Printed Message",
			Translate: true,
			Help:      "Message printed on the follow-up report",
		}),
		orm.Text("email_body", orm.FieldOpts{
			String:    "Email Body",
			Translate: true,
			Help:      "Email body for the follow-up email",
		}),
		orm.Char("email_subject", orm.FieldOpts{
			String:    "Email Subject",
			Translate: true,
		}),
		orm.Boolean("send_email", orm.FieldOpts{
			String:  "Send Email",
			Default: true,
		}),
		orm.Boolean("send_letter", orm.FieldOpts{
			String:  "Send Letter",
			Default: false,
		}),
		orm.Boolean("manual_action", orm.FieldOpts{
			String: "Manual Action",
			Help:   "Assign a manual action to be done",
		}),
		orm.Text("manual_action_note", orm.FieldOpts{
			String: "Action To Do",
			Help:   "Description of the manual action to be taken",
		}),
		orm.Many2one("manual_action_responsible_id", "res.users", orm.FieldOpts{
			String: "Assign a Responsible",
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true,
		}),
		orm.Selection("sms_template", []orm.SelectionItem{
			{Value: "none", Label: "None"},
			{Value: "default", Label: "Default"},
		}, orm.FieldOpts{String: "SMS Template", Default: "none"}),
		orm.Boolean("join_invoices", orm.FieldOpts{
			String:  "Attach Invoices",
			Default: false,
			Help:    "Attach open invoice PDFs to the follow-up email",
		}),
		orm.Selection("auto_execute", []orm.SelectionItem{
			{Value: "auto", Label: "Automatic"},
			{Value: "manual", Label: "Manual"},
		}, orm.FieldOpts{String: "Execution", Default: "manual"}),
	)

	// -- Follow-up Report (partner-level) --
	// Extends res.partner with follow-up state tracking.
	initFollowupPartnerExtension()

	// -- Follow-up processing method on partner --
	initFollowupProcess()
}

// initFollowupPartnerExtension adds follow-up tracking fields to res.partner.
func initFollowupPartnerExtension() {
	ext := orm.ExtendModel("res.partner")
	ext.AddFields(
		orm.Selection("followup_status", []orm.SelectionItem{
			{Value: "no_action_needed", Label: "No Action Needed"},
			{Value: "with_overdue_invoices", Label: "With Overdue Invoices"},
			{Value: "in_need_of_action", Label: "In Need of Action"},
		}, orm.FieldOpts{String: "Follow-up Status", Compute: "_compute_followup_status"}),
		orm.Many2one("followup_level_id", "account.followup.line", orm.FieldOpts{
			String: "Follow-up Level",
		}),
		orm.Date("followup_next_action_date", orm.FieldOpts{
			String: "Next Follow-up Date",
		}),
		orm.Text("followup_reminder", orm.FieldOpts{
			String: "Customer Follow-up Note",
		}),
		orm.Many2one("followup_responsible_id", "res.users", orm.FieldOpts{
			String: "Follow-up Responsible",
		}),
		orm.Integer("total_overdue_invoices", orm.FieldOpts{
			String: "Total Overdue Invoices", Compute: "_compute_total_overdue",
		}),
		orm.Monetary("total_overdue_amount", orm.FieldOpts{
			String: "Total Overdue Amount", Compute: "_compute_total_overdue",
		}),
	)

	// _compute_followup_status
	ext.RegisterCompute("followup_status", func(rs *orm.Recordset) (orm.Values, error) {
		env := rs.Env()
		partnerID := rs.IDs()[0]

		var overdueCount int
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COUNT(*) FROM account_move m
			 WHERE m.partner_id = $1 AND m.state = 'posted'
			 AND m.move_type IN ('out_invoice', 'out_receipt')
			 AND m.payment_state NOT IN ('paid', 'in_payment', 'reversed')
			 AND m.invoice_date_due < CURRENT_DATE`, partnerID,
		).Scan(&overdueCount)

		status := "no_action_needed"
		if overdueCount > 0 {
			// Check if there's a follow-up level set
			var levelID *int64
			env.Tx().QueryRow(env.Ctx(),
				`SELECT followup_level_id FROM res_partner WHERE id = $1`, partnerID,
			).Scan(&levelID)

			if levelID != nil && *levelID > 0 {
				status = "in_need_of_action"
			} else {
				status = "with_overdue_invoices"
			}
		}

		return orm.Values{"followup_status": status}, nil
	})

	// _compute_total_overdue: computes both count and amount of overdue invoices
	computeOverdue := func(rs *orm.Recordset) (orm.Values, error) {
		env := rs.Env()
		partnerID := rs.IDs()[0]

		var count int
		var total float64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COUNT(*), COALESCE(SUM(amount_residual::float8), 0)
			 FROM account_move
			 WHERE partner_id = $1 AND state = 'posted'
			 AND move_type IN ('out_invoice', 'out_receipt')
			 AND payment_state NOT IN ('paid', 'in_payment', 'reversed')
			 AND invoice_date_due < CURRENT_DATE`, partnerID,
		).Scan(&count, &total)

		return orm.Values{
			"total_overdue_invoices": count,
			"total_overdue_amount":   total,
		}, nil
	}
	ext.RegisterCompute("total_overdue_invoices", computeOverdue)
	ext.RegisterCompute("total_overdue_amount", computeOverdue)
}

// initFollowupProcess registers the follow-up processing methods.
func initFollowupProcess() {
	ext := orm.ExtendModel("res.partner")

	// action_execute_followup: run follow-up for the partner.
	// Mirrors: odoo/addons/account_followup/models/res_partner.py execute_followup()
	ext.RegisterMethod("action_execute_followup", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, partnerID := range rs.IDs() {
			// Find overdue invoices for this partner
			rows, err := env.Tx().Query(env.Ctx(),
				`SELECT m.id, m.name, m.invoice_date_due, m.amount_residual::float8,
				        CURRENT_DATE - m.invoice_date_due as overdue_days
				 FROM account_move m
				 WHERE m.partner_id = $1 AND m.state = 'posted'
				 AND m.move_type IN ('out_invoice', 'out_receipt')
				 AND m.payment_state NOT IN ('paid', 'in_payment', 'reversed')
				 AND m.invoice_date_due < CURRENT_DATE
				 ORDER BY m.invoice_date_due`, partnerID)
			if err != nil {
				return nil, fmt.Errorf("account: query overdue invoices for partner %d: %w", partnerID, err)
			}

			var maxOverdueDays int
			var overdueInvoiceIDs []int64
			for rows.Next() {
				var invID int64
				var invName string
				var dueDate interface{}
				var residual float64
				var overdueDays int
				if err := rows.Scan(&invID, &invName, &dueDate, &residual, &overdueDays); err != nil {
					continue
				}
				overdueInvoiceIDs = append(overdueInvoiceIDs, invID)
				if overdueDays > maxOverdueDays {
					maxOverdueDays = overdueDays
				}
			}
			rows.Close()

			if len(overdueInvoiceIDs) == 0 {
				continue
			}

			// Find the appropriate follow-up level based on overdue days
			var companyID int64
			env.Tx().QueryRow(env.Ctx(),
				`SELECT COALESCE(company_id, 1) FROM res_partner WHERE id = $1`, partnerID,
			).Scan(&companyID)

			var levelID *int64
			var levelName *string
			env.Tx().QueryRow(env.Ctx(),
				`SELECT id, name FROM account_followup_line
				 WHERE company_id = $1 AND delay <= $2
				 ORDER BY delay DESC LIMIT 1`, companyID, maxOverdueDays,
			).Scan(&levelID, &levelName)

			if levelID != nil {
				env.Tx().Exec(env.Ctx(),
					`UPDATE res_partner SET followup_level_id = $1,
					 followup_next_action_date = CURRENT_DATE + INTERVAL '14 days'
					 WHERE id = $2`, *levelID, partnerID)
			}
		}
		return true, nil
	})

	// get_followup_html: generate HTML report of overdue invoices for a partner.
	// Mirrors: odoo/addons/account_followup/models/res_partner.py get_followup_html()
	ext.RegisterMethod("get_followup_html", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		partnerID := rs.IDs()[0]

		var partnerName string
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(name, 'Unknown') FROM res_partner WHERE id = $1`, partnerID,
		).Scan(&partnerName)

		rows, err := env.Tx().Query(env.Ctx(),
			`SELECT m.name, m.invoice_date_due, m.amount_residual::float8,
			        m.amount_total::float8, CURRENT_DATE - m.invoice_date_due as overdue_days
			 FROM account_move m
			 WHERE m.partner_id = $1 AND m.state = 'posted'
			 AND m.move_type IN ('out_invoice', 'out_receipt')
			 AND m.payment_state NOT IN ('paid', 'in_payment', 'reversed')
			 ORDER BY m.invoice_date_due`, partnerID)
		if err != nil {
			return "", fmt.Errorf("account: query invoices for followup: %w", err)
		}
		defer rows.Close()

		var b strings.Builder
		b.WriteString(`<style>
			body{font-family:Arial;margin:20px}
			table{width:100%;border-collapse:collapse;margin-top:12px}
			th,td{border:1px solid #ddd;padding:6px 8px;text-align:right}
			th{background:#875a7b;color:white}
			td:first-child,th:first-child{text-align:left}
			.overdue{color:#d9534f;font-weight:bold}
			h2{color:#875a7b}
		</style>`)
		b.WriteString(fmt.Sprintf("<h2>Payment Follow-up: %s</h2>", partnerName))
		b.WriteString(`<table><tr><th>Invoice</th><th>Due Date</th><th>Total</th><th>Amount Due</th><th>Overdue Days</th></tr>`)

		var totalDue float64
		for rows.Next() {
			var invName string
			var dueDate interface{}
			var residual, total float64
			var overdueDays int
			if err := rows.Scan(&invName, &dueDate, &residual, &total, &overdueDays); err != nil {
				continue
			}
			totalDue += residual

			overdueClass := ""
			if overdueDays > 30 {
				overdueClass = ` class="overdue"`
			}
			b.WriteString(fmt.Sprintf(`<tr><td>%s</td><td>%v</td><td>%.2f</td><td>%.2f</td><td%s>%d</td></tr>`,
				invName, dueDate, total, residual, overdueClass, overdueDays))
		}

		b.WriteString(fmt.Sprintf(`<tr style="font-weight:bold;background:#f5f5f5"><td colspan="3">Total Due</td><td>%.2f</td><td></td></tr>`, totalDue))
		b.WriteString("</table>")

		// Add follow-up message if a level is set
		var description *string
		env.Tx().QueryRow(env.Ctx(),
			`SELECT fl.description FROM res_partner p
			 JOIN account_followup_line fl ON fl.id = p.followup_level_id
			 WHERE p.id = $1`, partnerID,
		).Scan(&description)

		if description != nil && *description != "" {
			b.WriteString(fmt.Sprintf(`<div style="margin-top:20px;padding:10px;background:#fff3cd;border:1px solid #ffc107;border-radius:4px">%s</div>`, *description))
		}

		return b.String(), nil
	})
}
