package adminfinance

import (
	"context"
	"encoding/json"
	"strings"
	"time"

	"gorm.io/gorm"
)

func (r *Repository) Disbursements(ctx context.Context, query DisbursementQuery) (*DisbursementListDTO, error) {
	var summary DisbursementListSummaryDTO
	summaryDB := r.applyDisbursementFilters(r.disbursementListBaseQuery(ctx), query)
	if err := summaryDB.Select(`COUNT(*) AS record_count,
		COALESCE(SUM(CASE WHEN status = 'paid' THEN actual_amount_cent ELSE 0 END), 0) AS paid_amount_cent,
		COALESCE(SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END), 0) AS paid_count,
		COALESCE(SUM(CASE WHEN status = 'payment_pending' THEN actual_amount_cent ELSE 0 END), 0) AS payment_pending_amount_cent,
		COALESCE(SUM(CASE WHEN status = 'payment_pending' THEN 1 ELSE 0 END), 0) AS payment_pending_count,
		COALESCE(SUM(CASE WHEN status = 'review_pending' THEN actual_amount_cent ELSE 0 END), 0) AS review_pending_amount_cent,
		COALESCE(SUM(CASE WHEN status = 'review_pending' THEN 1 ELSE 0 END), 0) AS review_pending_count,
		COALESCE(SUM(CASE WHEN source_type = 'withdrawal' AND status = 'paid' THEN fee_cent ELSE 0 END), 0) AS withdrawal_fee_amount_cent`).
		Scan(&summary).Error; err != nil {
		return nil, err
	}

	rows := make([]disbursementItemRow, 0, query.PageSize)
	orderColumn := "business_created_at"
	if query.DateType == "paid" {
		orderColumn = "paid_at"
	}
	offset := (query.Page - 1) * query.PageSize
	rowsDB := r.applyDisbursementFilters(r.disbursementListBaseQuery(ctx), query)
	if err := rowsDB.Order(orderColumn + " DESC").
		Order("source_id DESC").
		Offset(offset).
		Limit(query.PageSize).
		Scan(&rows).Error; err != nil {
		return nil, err
	}

	items := make([]DisbursementItemDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, row.toDTO())
	}
	return &DisbursementListDTO{
		Items:    items,
		Total:    summary.RecordCount,
		Page:     query.Page,
		PageSize: query.PageSize,
		Summary:  summary,
	}, nil
}

func (r *Repository) disbursementListBaseQuery(ctx context.Context) *gorm.DB {
	db := r.db.WithContext(ctx)
	platformManaged := db.Table("rental_orders AS ro").
		Select(`'platform_managed' AS source_type,
			ro.id AS source_id,
			ro.order_no AS business_no,
			ro.id AS order_id,
			0 AS withdrawal_id,
			ro.owner_id AS user_id,
			'外部卖家' AS payee_name,
			'' AS payee_phone,
			COALESCE(lu.uploader_name, '') AS uploader_name,
			COALESCE(lu.source_channel, '') AS source_channel,
			'' AS account_type,
			'' AS account_name,
			'' AS account_no,
			'' AS bank_name,
			ro.offline_settlement_amount_cent AS amount_cent,
			0 AS fee_cent,
			ro.offline_settlement_amount_cent AS actual_amount_cent,
			CASE WHEN ro.offline_settlement_status = 'settled' THEN 'paid' ELSE 'payment_pending' END AS status,
			ro.offline_settlement_status AS raw_status,
			ro.settled_at AS business_created_at,
			ro.offline_settled_at AS paid_at,
			ro.offline_settled_by AS operator_id,
			COALESCE(NULLIF(operator.nickname, ''), operator.username, '') AS operator_name,
			ro.offline_settlement_remark AS remark,
			'' AS category,
			'' AS custom_category_name,
			'' AS voucher_url,
			NULL AS voided_at,
			NULL AS voided_by,
			'' AS voided_by_name,
			'' AS void_reason,
			lu.parsed_payload AS source_payload`).
		Joins(`LEFT JOIN listing_uploads AS lu ON lu.id = (
			SELECT MAX(lu2.id) FROM listing_uploads AS lu2 WHERE lu2.listing_id = ro.listing_id
		)`).
		Joins("LEFT JOIN admin_users AS operator ON operator.id = ro.offline_settled_by").
		Where("ro.settlement_mode = ?", "platform_managed").
		Where("ro.offline_settlement_status IN ?", []string{"pending", "settled"}).
		Where("ro.offline_settlement_amount_cent > 0")

	pickupManaged := db.Table("admin_pickups AS ap").
		Select(`'pickup' AS source_type,
			ap.id AS source_id,
			ap.pickup_no AS business_no,
			0 AS order_id,
			0 AS withdrawal_id,
			ap.owner_id AS user_id,
			'外部卖家' AS payee_name,
			'' AS payee_phone,
			'' AS uploader_name,
			ap.source_channel,
			'' AS account_type,
			'' AS account_name,
			'' AS account_no,
			'' AS bank_name,
			ap.settle_amount_cent AS amount_cent,
			0 AS fee_cent,
			ap.settle_amount_cent AS actual_amount_cent,
			CASE WHEN ap.offline_settlement_status = 'settled' THEN 'paid' ELSE 'payment_pending' END AS status,
			ap.offline_settlement_status AS raw_status,
			ap.completed_at AS business_created_at,
			ap.offline_settled_at AS paid_at,
			ap.offline_settled_by AS operator_id,
			COALESCE(NULLIF(operator.nickname, ''), operator.username, '') AS operator_name,
			ap.offline_settlement_remark AS remark,
			'' AS category,
			'' AS custom_category_name,
			'' AS voucher_url,
			NULL AS voided_at,
			NULL AS voided_by,
			'' AS voided_by_name,
			'' AS void_reason,
			ap.account_snapshot AS source_payload`).
		Joins("LEFT JOIN admin_users AS operator ON operator.id = ap.offline_settled_by").
		Where("ap.settlement_mode = ?", "platform_managed").
		Where("ap.offline_settlement_status IN ?", []string{"pending", "settled"}).
		Where("ap.settle_amount_cent > 0")

	pickupAdjustments := db.Table("admin_pickup_financial_adjustments AS fa").
		Select(`'pickup_adjustment' AS source_type,
			fa.id AS source_id,
			CONCAT(ap.pickup_no, '-ADJ-', fa.id) AS business_no,
			0 AS order_id,
			0 AS withdrawal_id,
			ap.owner_id AS user_id,
			'外部卖家' AS payee_name,
			'' AS payee_phone,
			'' AS uploader_name,
			ap.source_channel,
			'' AS account_type,
			'' AS account_name,
			'' AS account_no,
			'' AS bank_name,
			fa.settle_delta_cent AS amount_cent,
			0 AS fee_cent,
			fa.settle_delta_cent AS actual_amount_cent,
			CASE WHEN fa.status = 'settled' THEN 'paid' ELSE 'payment_pending' END AS status,
			fa.status AS raw_status,
			fa.created_at AS business_created_at,
			fa.settled_at AS paid_at,
			fa.settled_by AS operator_id,
			COALESCE(NULLIF(operator.nickname, ''), operator.username, '') AS operator_name,
			fa.reason AS remark,
			'' AS category,
			'' AS custom_category_name,
			'' AS voucher_url,
			NULL AS voided_at,
			NULL AS voided_by,
			'' AS voided_by_name,
			'' AS void_reason,
			ap.account_snapshot AS source_payload`).
		Joins("JOIN admin_pickups AS ap ON ap.id = fa.pickup_id").
		Joins("LEFT JOIN admin_users AS operator ON operator.id = fa.settled_by").
		Where("fa.settlement_mode = ?", "platform_managed").
		Where("fa.status IN ?", []string{"pending", "settled"}).
		Where("fa.settle_delta_cent > 0")

	withdrawal := db.Table("withdrawal_requests AS wr").
		Select(`'withdrawal' AS source_type,
			wr.id AS source_id,
			wr.withdraw_no AS business_no,
			0 AS order_id,
			wr.id AS withdrawal_id,
			wr.user_id,
			COALESCE(NULLIF(u.nickname, ''), NULLIF(wr.account_name, ''), '') AS payee_name,
			COALESCE(u.phone, '') AS payee_phone,
			'' AS uploader_name,
			'' AS source_channel,
			wr.account_type,
			wr.account_name,
			wr.account_no,
			wr.bank_name,
			wr.amount_cent,
			wr.fee_cent,
			wr.actual_amount_cent,
			CASE
				WHEN wr.status = 'pending' THEN 'review_pending'
				WHEN wr.status = 'processing' THEN 'payment_pending'
				WHEN wr.status = 'completed' THEN 'paid'
				ELSE wr.status
			END AS status,
			wr.status AS raw_status,
			wr.created_at AS business_created_at,
			wr.paid_at,
			COALESCE(wr.paid_by, wr.reviewed_by) AS operator_id,
			COALESCE(NULLIF(operator.nickname, ''), operator.username, '') AS operator_name,
			CASE WHEN wr.payment_remark <> '' THEN wr.payment_remark ELSE wr.review_remark END AS remark,
			'' AS category,
			'' AS custom_category_name,
			'' AS voucher_url,
			NULL AS voided_at,
			NULL AS voided_by,
			'' AS voided_by_name,
			'' AS void_reason,
			NULL AS source_payload`).
		Joins("LEFT JOIN users AS u ON u.id = wr.user_id").
		Joins("LEFT JOIN admin_users AS operator ON operator.id = COALESCE(wr.paid_by, wr.reviewed_by)")

	manualOffline := db.Table("manual_disbursements AS md").
		Select(`'manual_offline' AS source_type,
			md.id AS source_id,
			md.disbursement_no AS business_no,
			0 AS order_id,
			0 AS withdrawal_id,
			0 AS user_id,
			md.payee_name,
			'' AS payee_phone,
			'' AS uploader_name,
			'' AS source_channel,
			'' AS account_type,
			'' AS account_name,
			'' AS account_no,
			'' AS bank_name,
			md.amount_cent,
			0 AS fee_cent,
			md.amount_cent AS actual_amount_cent,
			md.status,
			md.status AS raw_status,
			md.created_at AS business_created_at,
			md.paid_at,
			md.created_by AS operator_id,
			COALESCE(NULLIF(creator.nickname, ''), creator.username, '') AS operator_name,
			md.remark,
			md.category,
			md.custom_category_name,
			md.voucher_url,
			md.voided_at,
			md.voided_by,
			COALESCE(NULLIF(voider.nickname, ''), voider.username, '') AS voided_by_name,
			md.void_reason,
			NULL AS source_payload`).
		Joins("LEFT JOIN admin_users AS creator ON creator.id = md.created_by").
		Joins("LEFT JOIN admin_users AS voider ON voider.id = md.voided_by")

	union := db.Raw("? UNION ALL ? UNION ALL ? UNION ALL ? UNION ALL ?", platformManaged, pickupManaged, pickupAdjustments, withdrawal, manualOffline)
	return db.Table("(?) AS d", union)
}

func (r *Repository) applyDisbursementFilters(db *gorm.DB, query DisbursementQuery) *gorm.DB {
	if query.SourceType != "" {
		db = db.Where("d.source_type = ?", query.SourceType)
	}
	if query.Status != "" {
		db = db.Where("d.status = ?", query.Status)
	}
	if query.BusinessNo != "" {
		db = db.Where("d.business_no LIKE ?", "%"+query.BusinessNo+"%")
	}
	if query.UserID > 0 {
		db = db.Where("d.user_id = ?", query.UserID)
	}
	if query.Keyword != "" {
		like := "%" + query.Keyword + "%"
		db = db.Where(`(d.business_no LIKE ? OR d.payee_name LIKE ? OR d.payee_phone LIKE ?
			OR d.uploader_name LIKE ? OR d.account_name LIKE ? OR d.account_no LIKE ?)`, like, like, like, like, like, like)
	}
	if query.Operator != "" {
		like := "%" + query.Operator + "%"
		db = db.Where("(d.operator_name LIKE ? OR CAST(d.operator_id AS CHAR) LIKE ?)", like, like)
	}
	if query.OperatorID > 0 {
		db = db.Where("d.operator_id = ?", query.OperatorID)
	}
	if !query.StartDate.IsZero() && !query.EndDate.IsZero() {
		if query.DateType == "paid" {
			db = db.Where("d.paid_at >= ? AND d.paid_at <= ?", query.StartDate, query.EndDate)
		} else {
			db = db.Where("d.business_created_at >= ? AND d.business_created_at <= ?", query.StartDate, query.EndDate)
		}
	}
	return db
}

type disbursementItemRow struct {
	SourceType         string
	SourceID           uint64
	BusinessNo         string
	OrderID            uint64
	WithdrawalID       uint64
	UserID             uint64
	PayeeName          string
	PayeePhone         string
	UploaderName       string
	SourceChannel      string
	AccountType        string
	AccountName        string
	AccountNo          string
	BankName           string
	AmountCent         int64
	FeeCent            int64
	ActualAmountCent   int64
	Status             string
	RawStatus          string
	BusinessCreatedAt  *time.Time
	PaidAt             *time.Time
	OperatorID         *uint64
	OperatorName       string
	Remark             string
	Category           string
	CustomCategoryName string
	VoucherURL         string
	VoidedAt           *time.Time
	VoidedBy           *uint64
	VoidedByName       string
	VoidReason         string
	SourcePayload      []byte
}

func (r disbursementItemRow) toDTO() DisbursementItemDTO {
	phone := strings.TrimSpace(r.PayeePhone)
	if phone == "" && r.SourceType == "platform_managed" && len(r.SourcePayload) > 0 {
		var payload struct {
			ContactPhone string `json:"contactPhone"`
		}
		if json.Unmarshal(r.SourcePayload, &payload) == nil {
			phone = strings.TrimSpace(payload.ContactPhone)
		}
	}
	return DisbursementItemDTO{
		SourceType:         r.SourceType,
		SourceID:           r.SourceID,
		BusinessNo:         r.BusinessNo,
		OrderID:            r.OrderID,
		WithdrawalID:       r.WithdrawalID,
		UserID:             r.UserID,
		PayeeName:          r.PayeeName,
		PayeePhone:         phone,
		UploaderName:       r.UploaderName,
		SourceChannel:      r.SourceChannel,
		AccountType:        r.AccountType,
		AccountName:        r.AccountName,
		AccountNo:          r.AccountNo,
		BankName:           r.BankName,
		AmountCent:         r.AmountCent,
		FeeCent:            r.FeeCent,
		ActualAmountCent:   r.ActualAmountCent,
		Status:             r.Status,
		RawStatus:          r.RawStatus,
		BusinessCreatedAt:  r.BusinessCreatedAt,
		PaidAt:             r.PaidAt,
		OperatorID:         r.OperatorID,
		OperatorName:       r.OperatorName,
		Remark:             r.Remark,
		Category:           r.Category,
		CustomCategoryName: r.CustomCategoryName,
		VoucherURL:         r.VoucherURL,
		VoidedAt:           r.VoidedAt,
		VoidedBy:           r.VoidedBy,
		VoidedByName:       r.VoidedByName,
		VoidReason:         r.VoidReason,
	}
}
