<?php

namespace App\Services\AgentSettlement;

use App\Models\AgentNode;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/** 代理账单：汇总下级代理在本期保留的占成。 */
final class SettlementBillDownlineShareBuilder
{
    public function __construct(
        private readonly SettlementPartyEnrichment $partyEnrichment,
    ) {}

    /**
     * @return array{
     *   total: int,
     *   items: list<array{owner_id: int, owner_label: string, share_profit: int}>
     * }
     */
    public function forBill(object $bill): array
    {
        if ((string) $bill->bill_type !== 'agent' || (string) $bill->owner_type !== 'agent') {
            return ['total' => 0, 'items' => []];
        }

        $ownerId = (int) $bill->owner_id;
        $periodId = (int) $bill->settlement_period_id;
        if ($ownerId <= 0 || $periodId <= 0) {
            return ['total' => 0, 'items' => []];
        }

        $owner = AgentNode::query()->find($ownerId);
        if ($owner === null) {
            return ['total' => 0, 'items' => []];
        }

        $descendantIds = AgentNode::query()
            ->where('admin_site_id', (int) $owner->admin_site_id)
            ->where('id', '!=', $ownerId)
            ->where('path', 'like', $owner->path.'%')
            ->pluck('id')
            ->map(static fn ($id): int => (int) $id)
            ->all();

        if ($descendantIds === []) {
            return ['total' => 0, 'items' => []];
        }

        $rows = DB::table('settlement_bills')
            ->where('settlement_period_id', $periodId)
            ->where('bill_type', 'agent')
            ->where('owner_type', 'agent')
            ->whereIn('owner_id', $descendantIds)
            ->orderBy('owner_id')
            ->get(['owner_id', 'meta_json']);

        if ($rows->isEmpty()) {
            return ['total' => 0, 'items' => []];
        }

        $agentIds = $rows->pluck('owner_id')->map(static fn ($id): int => (int) $id)->all();
        $agents = $this->partyEnrichment->loadAgents($agentIds);

        $items = [];
        $total = 0;
        foreach ($rows as $row) {
            $shareProfit = $this->shareProfitFromMeta($row->meta_json ?? null);
            if ($shareProfit === 0) {
                continue;
            }

            $agentId = (int) $row->owner_id;
            $items[] = [
                'owner_id' => $agentId,
                'owner_label' => $this->partyEnrichment->formatAgent($agents->get($agentId), $agentId),
                'share_profit' => $shareProfit,
            ];
            $total += $shareProfit;
        }

        usort($items, static fn (array $a, array $b): int => $b['share_profit'] <=> $a['share_profit']
            ?: $a['owner_label'] <=> $b['owner_label']);

        return [
            'total' => $total,
            'items' => $items,
        ];
    }

    private function shareProfitFromMeta(mixed $metaJson): int
    {
        if ($metaJson === null || $metaJson === '') {
            return 0;
        }

        $decoded = is_string($metaJson) ? json_decode($metaJson, true) : $metaJson;

        return is_array($decoded) ? (int) ($decoded['share_profit'] ?? 0) : 0;
    }
}
