<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;

/**
 * Singleton settings row for AI automation toggles + schedule. The drafter,
 * auto-reply job, and shell executor all consult this row to decide whether
 * they should run and (for auto-reply) whether the current time is inside
 * the staff's office hours.
 *
 * Always access via ::current() — the table holds exactly one row and we
 * lazy-create it if missing (e.g. after a fresh dev DB).
 */
class AiAutomationSettings extends Model
{
    protected $table = 'ai_automation_settings';

    public const MODE_ALWAYS               = 'always';
    public const MODE_OUTSIDE_OFFICE_HOURS = 'outside_office_hours';
    public const MODE_CUSTOM_WINDOW        = 'custom_window';

    // AI server-action policy values. Read by chat/audit/scan paths that
    // create AiAction rows or auto-execute them. Default (set in the
    // migration) is POLICY_PROPOSE_ONLY — the safest middle ground.
    public const POLICY_BLOCK           = 'block';
    public const POLICY_PROPOSE_ONLY    = 'propose_only';
    public const POLICY_AUTO_LOW_RISK   = 'auto_low_risk_ok';

    protected $fillable = [
        'auto_draft_enabled',
        'auto_reply_enabled',
        'auto_reply_only_when_offline',
        'auto_reply_mode',
        'auto_reply_delay_minutes',
        'office_hours_start',
        'office_hours_end',
        'office_days',
        'custom_window_start',
        'custom_window_end',
        'nudge_waiting_customer_enabled',
        'nudge_interval_hours',
        'nudge_max_count',
        'admin_note_assist_enabled',
        'ns_diagnostic_enabled',
        'email_diagnostic_enabled',
        'admin_note_assist_departments',
        'admin_note_assist_interval_hours',
        'admin_note_assist_use_schedule',
        'admin_note_window_start',
        'admin_note_window_end',
        'admin_note_window_days',
        'min_confidence',
        'live_execution',
        'ai_server_action_policy',
        'updated_by',
    ];

    protected $casts = [
        'auto_draft_enabled'             => 'boolean',
        'auto_reply_enabled'             => 'boolean',
        'auto_reply_only_when_offline'   => 'boolean',
        'office_days'                    => 'array',
        'min_confidence'                 => 'float',
        'live_execution'                 => 'boolean',
        'nudge_waiting_customer_enabled' => 'boolean',
        'admin_note_assist_enabled'      => 'boolean',
        'ns_diagnostic_enabled'          => 'boolean',
        'email_diagnostic_enabled'       => 'boolean',
        'admin_note_assist_departments'  => 'array',
        'admin_note_assist_use_schedule' => 'boolean',
        'admin_note_window_days'         => 'array',
    ];

    public const ALL_DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

    /**
     * Singleton accessor. Cached for the request so the dozen call sites
     * that read settings don't re-query.
     */
    public static function current(): self
    {
        static $cached = null;
        if ($cached) return $cached;

        $row = self::query()->first();
        if (! $row) {
            $row = self::create([
                'auto_draft_enabled'           => true,
                'auto_reply_enabled'           => false,
                'auto_reply_only_when_offline' => true,
                'office_hours_start'           => '09:00:00',
                'office_hours_end'             => '22:00:00',
                'office_days'                  => self::ALL_DAYS,
                'min_confidence'               => 0.80,
                'live_execution'               => false,
            ]);
        }
        return $cached = $row;
    }

    /**
     * "Are staff currently online?" — yes if `now` (Asia/Dhaka) falls inside
     * the configured office_hours_start/end on a configured office day.
     * Used by AutoReplyTicket to decide whether to post when
     * auto_reply_only_when_offline=true.
     */
    public function staffOnline(): bool
    {
        $now = Carbon::now('Asia/Dhaka');
        $day = strtolower($now->format('D'));   // mon, tue, ...
        $days = $this->office_days ?: self::ALL_DAYS;
        if (! in_array($day, $days, true)) return false;

        $start = Carbon::createFromFormat('H:i:s', $this->office_hours_start ?: '09:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);
        $end   = Carbon::createFromFormat('H:i:s', $this->office_hours_end ?: '22:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);

        // Office hours that wrap past midnight (e.g. 22:00 → 06:00) are
        // handled by treating end<start as "spans midnight".
        if ($end->lt($start)) {
            return $now->gte($start) || $now->lte($end);
        }
        return $now->between($start, $end);
    }

    /**
     * "Is `now` inside the custom auto-reply window?" — for the
     * MODE_CUSTOM_WINDOW mode. Window can wrap past midnight, e.g.
     * 22:00–09:00 means "from 10pm tonight to 9am tomorrow".
     */
    public function inCustomWindow(): bool
    {
        $now = \Carbon\Carbon::now('Asia/Dhaka');
        $start = \Carbon\Carbon::createFromFormat('H:i:s', $this->custom_window_start ?: '22:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);
        $end   = \Carbon\Carbon::createFromFormat('H:i:s', $this->custom_window_end ?: '09:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);
        if ($end->lt($start)) {
            return $now->gte($start) || $now->lte($end);
        }
        return $now->between($start, $end);
    }

    /**
     * "Is now inside the admin-note assist time window?"
     *
     * Returns true when:
     *   - The feature is enabled AND
     *   - admin_note_assist_use_schedule is OFF (run 24/7) OR
     *   - now (Asia/Dhaka) falls inside the window on a configured day.
     *
     * Window wraps past midnight, mirroring the auto-reply custom window.
     */
    public function shouldRunAdminNoteAssist(): bool
    {
        if (! $this->admin_note_assist_enabled) return false;
        if (! $this->admin_note_assist_use_schedule) return true;

        $now = \Carbon\Carbon::now('Asia/Dhaka');
        $day = strtolower($now->format('D'));
        $days = $this->admin_note_window_days ?: self::ALL_DAYS;
        if (! in_array($day, $days, true)) return false;

        $start = \Carbon\Carbon::createFromFormat('H:i:s', $this->admin_note_window_start ?: '09:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);
        $end   = \Carbon\Carbon::createFromFormat('H:i:s', $this->admin_note_window_end ?: '22:00:00', 'Asia/Dhaka')
                       ->setDate($now->year, $now->month, $now->day);
        if ($end->lt($start)) {
            return $now->gte($start) || $now->lte($end);
        }
        return $now->between($start, $end);
    }

    /**
     * "Should AutoReplyTicket actually post the draft?" — combines the
     * master toggle with whichever schedule mode is selected.
     *   - always               → always yes (when master toggle is on)
     *   - outside_office_hours → yes when staff is not in office hours
     *   - custom_window        → yes when now falls inside the custom window
     *
     * The legacy auto_reply_only_when_offline column stays as a fallback
     * for rows that pre-date the mode field.
     */
    public function shouldAutoReplyNow(): bool
    {
        if (! $this->auto_reply_enabled) return false;

        $mode = $this->auto_reply_mode
            ?: ($this->auto_reply_only_when_offline ? self::MODE_OUTSIDE_OFFICE_HOURS : self::MODE_ALWAYS);

        return match ($mode) {
            self::MODE_ALWAYS               => true,
            self::MODE_OUTSIDE_OFFICE_HOURS => ! $this->staffOnline(),
            self::MODE_CUSTOM_WINDOW        => $this->inCustomWindow(),
            default                         => false,
        };
    }

    /**
     * "Can the AI itself queue/auto-run a server action right now?"
     *
     *   - POLICY_BLOCK        → no, ever. Operator can still type commands
     *                           in chat and click Run; that's a human ask.
     *   - POLICY_PROPOSE_ONLY → AI may create pending actions, but the
     *                           auto-execute flag is forced to false.
     *   - POLICY_AUTO_LOW_RISK→ legacy: auto_execute + risk=low may run.
     *
     * Callers pass the `kind`: 'autonomous' (scan/audit-driven) or
     * 'operator_chat' (the operator typed in chat with intent=action).
     * Operator chat is gentler — block still allows proposing because the
     * operator just asked the AI to do something, which is "ami bolle".
     * Even in chat-action mode, block prevents auto_execute and only
     * lets the operator click Run on the pending row.
     */
    public function mayAiProposeServerAction(string $kind = 'autonomous'): bool
    {
        $policy = $this->ai_server_action_policy ?: self::POLICY_PROPOSE_ONLY;
        if ($policy === self::POLICY_BLOCK) {
            // In block mode, scan/audit autonomous paths are fully off.
            // Operator chat may still propose (operator explicitly asked).
            return $kind === 'operator_chat';
        }
        return true;
    }

    public function mayAiAutoExecuteServerAction(): bool
    {
        $policy = $this->ai_server_action_policy ?: self::POLICY_PROPOSE_ONLY;
        return $policy === self::POLICY_AUTO_LOW_RISK;
    }
}
