package finance

import (
	"time"

	"github.com/jackc/pgx/v5/pgtype"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)
// effectivePaymentTerms normalizes display terms: POS (till) orders are always
// cash regardless of stored snapshotted value. Financial rows are immutable.
func effectivePaymentTerms(orderType string, terms int32) int32 {
	if orderType == "pos" {
		return 0
	}
	return terms
}


func invoiceFromRow(r db.GetInvoiceByIDRow) *Invoice {
	return &Invoice{
		ID: r.ID, InvoiceNumber: r.InvoiceNumber, SalesOrderID: r.SalesOrderID.Int64,
		CustomerID: r.CustomerID, InvoiceDate: r.InvoiceDate, DueDate: r.DueDate, Status: r.Status,
		Subtotal: r.Subtotal, TaxAmount: r.TaxAmount, TotalAmount: r.TotalAmount,
		PaidAmount: r.PaidAmount, RemainingAmount: r.RemainingAmount, Notes: textToStrPtr(r.Notes),
		CreatedAt: r.CreatedAt.Time, UpdatedAt: r.UpdatedAt.Time,
	}
}

func invoiceFromListRow(r db.ListInvoicesRow) Invoice {
	return Invoice{
		ID: r.ID, InvoiceNumber: r.InvoiceNumber, SalesOrderID: r.SalesOrderID.Int64,
		CustomerID: r.CustomerID, InvoiceDate: r.InvoiceDate, DueDate: r.DueDate, Status: r.Status,
		Subtotal: r.Subtotal, TaxAmount: r.TaxAmount, TotalAmount: r.TotalAmount,
		PaidAmount: r.PaidAmount, RemainingAmount: r.RemainingAmount, Notes: textToStrPtr(r.Notes),
		CreatedAt: r.CreatedAt.Time, UpdatedAt: r.UpdatedAt.Time,
		CustomerName: r.CustomerName, SoNumber: r.SoNumber,
		StoreID: int8ToPtr(r.StoreID), StoreName: r.StoreName, OrderType: r.OrderType,
	}
}

func invoiceItemFromRow(r db.ListInvoiceItemsRow) InvoiceItem {
	return InvoiceItem{
		ID: r.ID, SalesOrderID: r.SalesOrderID, ProductID: r.ProductID,
		ProductCode: r.ProductCode, ProductName: r.ProductName,
		Quantity: r.Quantity, UnitPrice: r.UnitPrice,
		DiscountPercent: r.DiscountPercent, DiscountAmount: r.DiscountAmount,
		Subtotal: r.Subtotal,
	}
}

func paymentFromRow(r db.ListPaymentsRow) Payment {
	return Payment{
		ID: r.ID, PaymentNumber: r.PaymentNumber, PayableType: r.PayableType, PayableID: r.PayableID,
		PaymentDate: r.PaymentDate, Amount: r.Amount, PaymentMethod: r.PaymentMethod,
		BankName: textToStrPtr(r.BankName), BankAccount: textToStrPtr(r.BankAccount),
		ReferenceNumber: textToStrPtr(r.ReferenceNumber), Notes: textToStrPtr(r.Notes),
		ProofURL: textToStrPtr(r.ProofUrl), CreatedBy: int8ToPtr(r.CreatedBy),
		PayableLabel: r.PayableLabel, CustomerName: r.CustomerName,
		StoreID: int8ToPtr(r.StoreID), StoreName: r.StoreName, OrderType: r.OrderType,
	}
}
func receivableFromRow(r db.ListReceivablesRow) ReceivableRow {


	return ReceivableRow{
		InvoiceID: r.ID, InvoiceNumber: r.InvoiceNumber, CustomerID: r.CustomerID, CustomerName: r.Name,
		CustomerPhone: textToStrPtr(r.Phone), CustomerAddress: r.CustomerAddress,
		InvoiceDate:   r.InvInvoiceDate, DueDate: r.InvDueDate, Status: r.Status,
		SoNumber: r.SoNumber, PaymentTerms: int4ToPtr(pgtype.Int4{Int32: effectivePaymentTerms(r.OrderType, int32(r.PaymentTerms.Int32)), Valid: r.PaymentTerms.Valid}), SalesName: textToStrPtr(r.SalesName),
		StoreID: int8ToPtr(r.StoreID), StoreName: r.StoreName, OrderType: r.OrderType,
		TotalAmount: r.TotalAmount, PaidAmount: r.PaidAmount, Remaining: r.RemainingAmount,
	}

}

func cashflowFromRow(r db.ListCashflowsRow) Cashflow {
	return Cashflow{
		ID: r.ID, TransactionNumber: r.TransactionNumber, Type: r.Type, Category: r.Category,
		TransactionDate: r.TransactionDate, Amount: r.Amount, Description: textToStrPtr(r.Description),
		ReferenceType: textToStrPtr(r.ReferenceType), ReferenceID: int8ToPtr(r.ReferenceID),
		ReversalOf: int8ToPtr(r.ReversalOf), CreatedBy: int8ToPtr(r.CreatedBy),
		StoreID: int8ToPtr(r.StoreID),
	}
}

func cashflowFromGetRow(r db.GetCashflowByIDRow) *Cashflow {
	return &Cashflow{
		ID: r.ID, TransactionNumber: r.TransactionNumber, Type: r.Type, Category: r.Category,
		TransactionDate: r.TransactionDate, Amount: r.Amount, Description: textToStrPtr(r.Description),
		ReferenceType: textToStrPtr(r.ReferenceType), ReferenceID: int8ToPtr(r.ReferenceID),
		ReversalOf: int8ToPtr(r.ReversalOf), CreatedBy: int8ToPtr(r.CreatedBy),
		StoreID: int8ToPtr(r.StoreID),
	}
}

func cashflowFromUpdateRow(r db.UpdateCashflowRow) *Cashflow {
	return cashflowFromGetRow(db.GetCashflowByIDRow(r))
}

func createInvoiceParams(inv *Invoice) db.CreateInvoiceParams {
	return db.CreateInvoiceParams{
		InvoiceNumber: inv.InvoiceNumber, SalesOrderID: pgtype.Int8{Int64: inv.SalesOrderID, Valid: true},
		CustomerID: inv.CustomerID, InvoiceDate: dateOrNil(inv.InvoiceDate), DueDate: dateOrNil(inv.DueDate),
		Status: inv.Status, Subtotal: inv.Subtotal, TaxAmount: inv.TaxAmount, TotalAmount: inv.TotalAmount,
		PaidAmount: inv.PaidAmount, RemainingAmount: inv.RemainingAmount, Notes: textOrNilStr(inv.Notes),
	}
}

func updateInvoiceParams(inv *Invoice) db.UpdateInvoiceParams {
	return db.UpdateInvoiceParams{
		ID: inv.ID, Status: inv.Status, PaidAmount: inv.PaidAmount,
		RemainingAmount: inv.RemainingAmount, Notes: textOrNilStr(inv.Notes),
	}
}

func createPaymentParams(p *Payment) db.CreatePaymentParams {
	return db.CreatePaymentParams{
		PaymentNumber: p.PaymentNumber, PayableType: p.PayableType, PayableID: p.PayableID,
		PaymentDate: dateOrNil(p.PaymentDate), Amount: p.Amount, PaymentMethod: p.PaymentMethod,
		BankName: textOrNilStr(p.BankName), BankAccount: textOrNilStr(p.BankAccount),
		ReferenceNumber: textOrNilStr(p.ReferenceNumber), Notes: textOrNilStr(p.Notes), CreatedBy: int8OrNil(p.CreatedBy),
	}
}

func createCashflowParams(c *Cashflow) db.CreateCashflowParams {
	return db.CreateCashflowParams{
		TransactionNumber:     c.TransactionNumber,
		Type:                  c.Type,
		Category:              c.Category,
		TransactionDate:       dateOrNil(c.TransactionDate),
		Amount:                c.Amount,
		Description:           textOrNilStr(c.Description),
		ReferenceType:         textOrNilStr(c.ReferenceType),
		ReferenceID:           int8OrNil(c.ReferenceID),
		CreatedBy:             int8OrNil(c.CreatedBy),
		ReversalOf:            int8OrNil(c.ReversalOf),
		AccountID:             int8OrNil(c.AccountID),
		CounterpartAccountID:  int8OrNil(c.CounterpartAccountID),
		FlowType:              textOrNilStr(c.FlowType),
		PaymentMethod:         textOrNilStr(c.PaymentMethod),
		AttachmentKey:         textOrNilStr(c.AttachmentKey),
		AttachmentFilename:    textOrNilStr(c.AttachmentFilename),
		AttachmentContentType: textOrNilStr(c.AttachmentContentType),
		AttachmentSize:        int8OrNil(c.AttachmentSize),
		AttachmentUploadedBy:  int8OrNil(c.AttachmentUploadedBy),
		// From the CALLER'S context, set by the service (cashflowWriteStore).
		// Not a CashflowInput field, and must never become one: a body that can
		// name its own outlet writes rows where it can then read them back.
		StoreID: int8OrNil(c.StoreID),
	}
}

func updateCashflowParams(id int64, in CashflowInput) db.UpdateCashflowParams {
	return db.UpdateCashflowParams{
		ID: id, Type: in.Type, Category: in.Category, TransactionDate: dateOrNil(in.TransactionDate),
		Amount: in.Amount, Description: textOrNilStr(in.Description),
	}
}

func textOrNil(v string) pgtype.Text {
	return pgtype.Text{String: v, Valid: v != ""}
}

func textOrNilStr(v *string) pgtype.Text {
	if v == nil {
		return pgtype.Text{}
	}
	return pgtype.Text{String: *v, Valid: true}
}

func textToStrPtr(v pgtype.Text) *string {
	if !v.Valid {
		return nil
	}
	return &v.String
}

func int8OrNil(v *int64) pgtype.Int8 {
	if v == nil {
		return pgtype.Int8{}
	}
	return pgtype.Int8{Int64: *v, Valid: true}
}

func int8ToPtr(v pgtype.Int8) *int64 {
	if !v.Valid {
		return nil
	}
	return &v.Int64
}

func int4ToPtr(v pgtype.Int4) *int32 {
	if !v.Valid {
		return nil
	}
	x := v.Int32
	return &x
}

func int4OrNil(v *int32) pgtype.Int4 {
	if v == nil {
		return pgtype.Int4{}
	}
	return pgtype.Int4{Int32: *v, Valid: true}
}

func dateOrNil(v string) pgtype.Date {
	t, err := time.Parse("2006-01-02", v)
	return pgtype.Date{Time: t, Valid: err == nil && v != ""}
}
