<?php

declare(strict_types=1);

namespace app\common\service;

use app\admin\model\split\Number;
use app\admin\model\split\Ticket;

/**
 * 工单与号码业务规则（单号上限、下号比率、时间窗口、完成量自动开关）
 */
class SplitTicketRuleService
{
    private SplitNumberRatioDeferredService $ratioDeferredService;

    public function __construct(?SplitNumberRatioDeferredService $ratioDeferredService = null)
    {
        $this->ratioDeferredService = $ratioDeferredService ?? new SplitNumberRatioDeferredService();
    }

    /**
     * 手动关闭的工单（manual_manage=1）：禁止自动同步改写 status，须用户手动开启
     */
    public function isManuallyClosed(Ticket $ticket): bool
    {
        return (int) ($ticket['manual_manage'] ?? 0) === 1;
    }

    /**
     * 列表/编辑切换状态时同步 manual_manage 标记
     *
     * @param array<string, mixed> $values
     */
    public function applyManualManageForStatusChange(array &$values): void
    {
        if (!isset($values['status'])) {
            return;
        }
        if ((string) $values['status'] === 'hidden') {
            $values['manual_manage'] = 1;
        } elseif ((string) $values['status'] === 'normal') {
            $values['manual_manage'] = 0;
        }
    }

    /**
     * 同步后应用工单开关规则；号码开关由 applyNumberRules 综合裁决（平台在线 + 单号上限 + 下号比率）
     */
    public function applyAfterSync(Ticket $ticket, int $completeCount): void
    {
        $this->applyTicketStatusRules($ticket, $completeCount);
        $fresh = Ticket::get((int) $ticket['id']);
        if ($fresh) {
            if ((string) $fresh['status'] === 'hidden') {
                $this->cascadeTicketClosedToNumbers($fresh);
            } else {
                $this->applyNumberRules($fresh);
            }
        }
    }

    /**
     * 同步流程：工单因时间/完成量等原因关闭时，联动关闭非手动号码
     */
    public function cascadeTicketClosedToNumbers(Ticket $ticket): void
    {
        if ((string) ($ticket['status'] ?? 'hidden') !== 'hidden') {
            return;
        }
        Number::where('admin_id', (int) $ticket['admin_id'])
            ->where('split_link_id', (int) $ticket['split_link_id'])
            ->where('ticket_name', (string) $ticket['ticket_name'])
            ->where('manual_manage', 0)
            ->update([
                'status'     => 'hidden',
                'updatetime' => time(),
            ]);

        (new SplitTicketActiveNumberCountService())->refreshForTicket($ticket);
    }

    /**
     * 工单物理删除时，移除该工单关联的全部号码（含 manual_manage=1）
     *
     * 关联键与 SplitTicketNumberSyncService 一致：admin_id + split_link_id + ticket_name
     *
     * @return int 删除行数
     */
    public function deleteSyncedNumbersForTicket(Ticket $ticket): int
    {
        $ticketName = trim((string) ($ticket['ticket_name'] ?? ''));
        $linkId = (int) ($ticket['split_link_id'] ?? 0);
        if ($ticketName === '' || $linkId <= 0) {
            return 0;
        }

        return Number::where('admin_id', (int) $ticket['admin_id'])
            ->where('split_link_id', $linkId)
            ->where('ticket_name', $ticketName)
            ->delete();
    }

    /**
     * 手动切换工单状态时，非手动号码按业务规则对齐开关
     */
    public function syncNumbersWithTicketStatus(Ticket $ticket): void
    {
        $ticketStatus = (string) ($ticket['status'] ?? 'hidden');
        if ($ticketStatus === 'hidden') {
            $this->cascadeTicketClosedToNumbers($ticket);
            return;
        }
        $this->applyNumberRules($ticket);
    }

    /**
     * 工单处于开启状态时，将云控在线的非手动号码设为开启
     *
     * @deprecated 请使用 applyNumberRules
     */
    public function syncOnlineNumbersWhenTicketOpen(Ticket $ticket): void
    {
        $this->applyNumberRules($ticket);
    }

    /**
     * 单号上限、下号比率、云控在线状态、工单开关综合决定号码 status；同步时清除或确认 ratio_deferred
     */
    public function applyNumberRules(Ticket $ticket): void
    {
        $orderLimit = (int) ($ticket['order_limit'] ?? 0);
        $assignRatio = (int) ($ticket['assign_ratio'] ?? 0);

        $numbers = Number::where('admin_id', (int) $ticket['admin_id'])
            ->where('split_link_id', (int) $ticket['split_link_id'])
            ->where('ticket_name', (string) $ticket['ticket_name'])
            ->select();

        foreach ($numbers as $number) {
            $visitCount = (int) $number['visit_count'];
            $inboundCount = (int) $number['inbound_count'];
            $lastInbound = (int) ($number['last_sync_inbound_count'] ?? 0);
            $streak = (int) ($number['no_inbound_click_streak'] ?? 0);

            if ($visitCount <= 0) {
                $streak = 0;
            } else {
                $streak = $this->ratioDeferredService->computeStreakForSync($number, $streak);
            }

            $update = [
                'no_inbound_click_streak' => $streak,
                'last_sync_visit_count'   => $visitCount,
                'last_sync_inbound_count' => $inboundCount,
                'updatetime'              => time(),
            ];

            $clearDeferred = [
                'ratio_deferred'    => 0,
                'ratio_deferred_at' => null,
            ];

            // 手动管理：更新统计；无效降权（无落地页访问）一并清除
            if ((int) $number['manual_manage'] === 1) {
                if ($this->ratioDeferredService->isInvalidDeferredRecord($number) || $visitCount <= 0) {
                    $update = array_merge($update, $clearDeferred);
                    if ($visitCount <= 0) {
                        $update['no_inbound_click_streak'] = 0;
                    }
                }
                Number::where('id', (int) $number['id'])->update($update);
                continue;
            }

            // 同步裁决 ratio_deferred：同步时清除访问侧降权，由 streak 决定是否关号
            $update = array_merge($update, $clearDeferred);

            $update['status'] = $this->resolveAutomatedStatus(
                $ticket,
                (string) ($number['platform_status'] ?? 'unknown'),
                $inboundCount,
                $streak,
                $orderLimit,
                $assignRatio
            );

            Number::where('id', (int) $number['id'])->update($update);
        }

        (new SplitTicketActiveNumberCountService())->refreshForTicket($ticket);
    }

    /**
     * 非手动管理号码的自动开关判定
     */
    private function resolveAutomatedStatus(
        Ticket $ticket,
        string $platformStatus,
        int $inboundCount,
        int $streak,
        int $orderLimit,
        int $assignRatio
    ): string {
        if ((string) ($ticket['status'] ?? 'hidden') !== 'normal') {
            return 'hidden';
        }
        if ($platformStatus !== 'online') {
            return 'hidden';
        }
        if ($orderLimit > 0 && $inboundCount >= $orderLimit) {
            return 'hidden';
        }
        if ($assignRatio > 0 && $streak >= $assignRatio) {
            return 'hidden';
        }

        return 'normal';
    }

    /**
     * 完成量、时间窗口决定工单开关（定时与手动同步均适用；手动关闭工单除外）
     */
    public function applyTicketStatusRules(Ticket $ticket, int $completeCount): void
    {
        if ($this->isManuallyClosed($ticket)) {
            return;
        }
        $status = $this->resolveTicketStatus($ticket, $completeCount);
        if ($status !== (string) ($ticket['status'] ?? 'hidden')) {
            Ticket::where('id', (int) $ticket['id'])->update([
                'status'     => $status,
                'updatetime' => time(),
            ]);
            $ticket['status'] = $status;
        }
    }

    /**
     * 是否处于允许同步/开启的时间窗口
     */
    public function isWithinTimeWindow(Ticket $ticket, ?int $now = null): bool
    {
        $now = $now ?? time();
        $start = $ticket['start_time'] ?? null;
        $end = $ticket['end_time'] ?? null;
        if ($start !== null && $start !== '' && (int) $start > 0 && $now < (int) $start) {
            return false;
        }
        if ($end !== null && $end !== '' && (int) $end > 0 && $now > (int) $end) {
            return false;
        }
        return true;
    }

    private function resolveTicketStatus(Ticket $ticket, int $completeCount): string
    {
        if (!$this->isWithinTimeWindow($ticket)) {
            return 'hidden';
        }

        $ticketTotal = (int) ($ticket['ticket_total'] ?? 0);
        if ($ticketTotal > 0) {
            return $completeCount >= $ticketTotal ? 'hidden' : 'normal';
        }

        return 'normal';
    }
}
