<?php
declare(strict_types=1);

namespace App\Console;

use App\Models\PmTemplate;
use App\Models\WorkOrder;
use Core\DB;

/**
 * Convert every PM schedule that's due into a real work order.
 * Run daily from cron: php bin/console pm:generate
 *
 * Two branches:
 *   - Calendar    → next_due_at ≤ (today + max lead)
 *   - Meter-based → latest plant_reading crossed
 *                   (last_meter_reading + meter_interval)
 */
final class PmGenerate
{
    public function run(array $argv = []): int
    {
        // ── Calendar-triggered schedules ──
        $maxLead = (int) DB::value('SELECT COALESCE(MAX(lead_days), 3) FROM pm_templates') ?: 3;
        $cutoffDate = date('Y-m-d 23:59:59', strtotime("+{$maxLead} days"));
        $calendarDue = PmTemplate::schedulesDueBefore($cutoffDate);

        // ── Meter-triggered schedules ──
        $meterDue = PmTemplate::meterSchedulesDue();

        if (!$calendarDue && !$meterDue) {
            fwrite(STDOUT, "No PM schedules due (calendar or meter).\n");
            return 0;
        }

        $created = 0; $skipped = 0;
        foreach ($calendarDue as $s) {
            [$didCreate, $reason] = $this->materialise($s, 'calendar');
            if ($didCreate) $created++; else $skipped++;
        }
        foreach ($meterDue as $s) {
            [$didCreate, $reason] = $this->materialise($s, 'meter');
            if ($didCreate) $created++; else $skipped++;
        }

        fwrite(STDOUT, "PM generate: {$created} created, {$skipped} skipped (already open).\n");
        return 0;
    }

    /**
     * Create a WO from a schedule row. Skips if a WO with the same title
     * already exists open for this asset (idempotency across repeat runs).
     * Advances the schedule appropriately after a successful create.
     */
    private function materialise(array $s, string $mode): array
    {
        $tenantId = (int) $s['tenant_id'];
        $assetId  = (int) $s['asset_id'];
        $title    = 'PM: ' . $s['template_name'] . ' — ' . $s['asset_name'];
        if ($mode === 'meter' && isset($s['current_meter'])) {
            // Suffix with the meter value so successive meter-triggered WOs
            // for the same asset don't collide by title.
            $title .= sprintf(' @ %s = %s', $s['meter_key'], rtrim(rtrim(number_format((float) $s['current_meter'], 3), '0'), '.'));
        }

        $exists = DB::value(
            'SELECT work_order_id FROM work_orders
              WHERE tenant_id = :t AND asset_id = :a AND wo_type = "preventive"
                AND status NOT IN ("completed","cancelled") AND deleted_at IS NULL
                AND title = :n LIMIT 1',
            ['t' => $tenantId, 'a' => $assetId, 'n' => $title]
        );
        if ($exists) return [false, 'already_open'];

        $woId = WorkOrder::create($tenantId, 0, [
            'title'        => $title,
            'description'  => $s['template_desc'] ?? null,
            'wo_type'      => 'preventive',
            'priority'     => $s['priority'] ?? 'medium',
            'status'       => 'assigned',
            'asset_id'     => $assetId,
            'assigned_to'  => !empty($s['assigned_to']) ? (int) $s['assigned_to'] : null,
            'due_at'       => $s['next_due_at'] ?? null,
            'scheduled_at' => $s['next_due_at'] ?? null,
        ]);

        foreach (PmTemplate::tasksFor((int) $s['template_id']) as $t) {
            WorkOrder::addTask($woId, [
                'title'             => $t['title'],
                'description'       => $t['description'],
                'sort_order'        => (int) $t['sort_order'],
                'input_type'        => $t['input_type']        ?? 'checkbox',
                'options_json'      => $t['options_json']      ?? null,
                'is_required'       => !empty($t['is_required']),
                'photo_requirement' => $t['photo_requirement'] ?? 'n_a',
            ]);
        }

        if ($mode === 'meter') {
            PmTemplate::advanceMeterSchedule((int) $s['schedule_id'], (float) $s['current_meter']);
        } else {
            PmTemplate::advanceSchedule((int) $s['schedule_id'], (int) $s['interval_days']);
        }
        return [true, 'created'];
    }
}
