<?php

namespace App\Support;

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

final class AgentNodePresenter
{
    /**
     * @return array{
     *   id: int,
     *   admin_site_id: int,
     *   parent_id: ?int,
     *   path: string,
     *   depth: int,
     *   code: string,
     *   name: string,
     *   status: int,
     *   is_root: bool,
     *   username: ?string,
     *   email: ?string
     * }
     */
    /**
     * @return array<string, mixed>
     */
    public static function profileSummary(AgentProfile $profile): array
    {
        return [
            'total_share_rate' => (float) $profile->total_share_rate,
            'credit_limit' => (int) $profile->credit_limit,
            'allocated_credit' => (int) $profile->allocated_credit,
            'used_credit' => (int) $profile->used_credit,
            'available_credit' => max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit),
            'rebate_limit' => round((float) $profile->rebate_limit * 100, 4),
            'default_player_rebate' => round((float) $profile->default_player_rebate * 100, 4),
        ];
    }

    public static function item(AgentNode $node, ?AgentProfile $profile = null): array
    {
        $account = DB::table('admin_user_agents as aua')
            ->join('admin_users as au', 'au.id', '=', 'aua.admin_user_id')
            ->where('aua.agent_node_id', $node->id)
            ->orderByDesc('aua.is_primary')
            ->orderBy('aua.admin_user_id')
            ->select('au.username', 'au.email')
            ->first();

        $siteCode = AdminSite::query()->where('id', $node->admin_site_id)->value('code');

        $payload = [
            'id' => (int) $node->id,
            'admin_site_id' => (int) $node->admin_site_id,
            'site_code' => $siteCode !== null ? (string) $siteCode : null,
            'parent_id' => $node->parent_id !== null ? (int) $node->parent_id : null,
            'path' => (string) $node->path,
            'depth' => (int) $node->depth,
            'code' => (string) $node->code,
            'name' => (string) $node->name,
            'status' => (int) $node->status,
            'is_root' => $node->isRoot(),
            'is_line_root' => $node->isRoot(),
            'username' => $account?->username !== null ? (string) $account->username : null,
            'email' => $account?->email !== null ? (string) $account->email : null,
        ];

        if ($profile !== null) {
            $payload['profile_summary'] = self::profileSummary($profile);
        }

        return $payload;
    }

    /**
     * @param  iterable<AgentNode>  $nodes
     * @return list<array<string, mixed>>
     */
    public static function tree(iterable $nodes): array
    {
        $nodeList = $nodes instanceof Collection ? $nodes : collect($nodes);
        $profiles = AgentProfile::query()
            ->whereIn('agent_node_id', $nodeList->pluck('id'))
            ->get()
            ->keyBy('agent_node_id');

        $items = [];
        $byParent = [];

        foreach ($nodeList as $node) {
            $profile = $profiles->get($node->id);
            $row = self::item($node, $profile instanceof AgentProfile ? $profile : null);
            $row['children'] = [];
            $items[(int) $node->id] = $row;
            $parentKey = $node->parent_id !== null ? (int) $node->parent_id : 0;
            $byParent[$parentKey][] = (int) $node->id;
        }

        $attach = static function (int $id) use (&$attach, &$items, $byParent): array {
            $node = $items[$id];
            foreach ($byParent[$id] ?? [] as $childId) {
                $node['children'][] = $attach($childId);
            }

            return $node;
        };

        $ids = array_keys($items);
        $rootIds = [];
        foreach ($items as $id => $row) {
            $parentId = $row['parent_id'];
            if ($parentId === null || ! in_array((int) $parentId, $ids, true)) {
                $rootIds[] = (int) $id;
            }
        }

        $roots = [];
        foreach ($rootIds as $rootId) {
            $roots[] = $attach($rootId);
        }

        return $roots;
    }

    /**
     * @param  iterable<AgentNode>  $nodes
     * @return list<array<string, mixed>>
     */
    public static function list(iterable $nodes): array
    {
        $nodeList = $nodes instanceof Collection ? $nodes : collect($nodes);
        $profiles = AgentProfile::query()
            ->whereIn('agent_node_id', $nodeList->pluck('id'))
            ->get()
            ->keyBy('agent_node_id');

        return $nodeList
            ->map(function (AgentNode $node) use ($profiles): array {
                $profile = $profiles->get($node->id);

                return self::item($node, $profile instanceof AgentProfile ? $profile : null);
            })
            ->values()
            ->all();
    }
}
