<?php

declare(strict_types=1);

namespace App\Services\Metrics;

use Carbon\CarbonInterface;
use Illuminate\Support\Facades\DB;

/**
 * Imputa el interes cobrado a la fecha del pago que lo genero.
 *
 * REGLA: INTERES PRIMERO. Para cada cuota se recorren sus aplicaciones
 * (payment_installment.applied_amount) en orden de payments.payment_date; el
 * acumulado cubre primero el interest_amount de la cuota y el resto es capital.
 * Es la misma convencion que ya usa `interest_earned`, asi que no crea una
 * segunda verdad.
 *
 * NO lee de `incomes`: hoy los pagos se registran con la categoria generica
 * `payment`, sin separar capital de interes — por eso "Utilidad mes" marcaba $0.
 * Ver docs/superpowers/specs/2026-09-08-dashboard-salud-negocio-design.md §A.
 */
class InterestAttributionService
{
    /** Interes cobrado entre dos fechas (ambas inclusive). */
    public function interestBetween(int $companyId, CarbonInterface $from, CarbonInterface $to): float
    {
        $row = DB::selectOne(
            $this->baseSql().' AND payment_date BETWEEN ? AND ?',
            [$companyId, $from->toDateString(), $to->toDateString()]
        );

        return (float) ($row->interes ?? 0.0);
    }

    /** Interes cobrado historico, sin filtro de fecha. Lo usa credify:verify-interest. */
    public function interestTotal(int $companyId): float
    {
        $row = DB::selectOne($this->baseSql(), [$companyId]);

        return (float) ($row->interes ?? 0.0);
    }

    /**
     * El acumulado de la ventana se calcula sobre TODAS las aplicaciones de la
     * cuota; el filtro de fecha va afuera, para que un pago del periodo sepa
     * cuanto interes quedaba pendiente antes de el.
     */
    private function baseSql(): string
    {
        return <<<'SQL'
            WITH app AS (
                SELECT
                    p.payment_date,
                    pi.applied_amount,
                    i.interest_amount,
                    SUM(pi.applied_amount) OVER (
                        PARTITION BY pi.installment_id ORDER BY p.payment_date, pi.id
                    ) AS cum_after,
                    SUM(pi.applied_amount) OVER (
                        PARTITION BY pi.installment_id ORDER BY p.payment_date, pi.id
                    ) - pi.applied_amount AS cum_before
                FROM payment_installment pi
                INNER JOIN payments p     ON p.id = pi.payment_id AND p.voided = 0
                INNER JOIN installments i ON i.id = pi.installment_id
                INNER JOIN credits c      ON c.id = i.credit_id
                WHERE c.company_id = ?
            )
            SELECT COALESCE(SUM(
                GREATEST(0, LEAST(cum_after, interest_amount) - LEAST(cum_before, interest_amount))
            ), 0) AS interes
            FROM app
            WHERE 1 = 1
            SQL;
    }
}
