<?php
defined('ABSPATH') or die('Direct access not allowed');

/**
 * VollstartRD_Checklists — Checklist templates and per-repair instances.
 *
 * SRP: Manages checklist templates (assigned to repair types) and
 * per-repair checklist instances (checkable steps with tracking).
 */
class VollstartRD_Checklists {

    private $MAIN;

    public function __construct($MAIN) {
        $this->MAIN = $MAIN;
    }

    // --- Checklist (Step List) CRUD ---

    public function createChecklist(array $data): int {
        return $this->MAIN->getDB()->insert('checklists', [
            'name'                 => sanitize_text_field($data['name'] ?? ''),
            'description'          => sanitize_textarea_field($data['description'] ?? ''),
            'trigger_status_start' => sanitize_text_field($data['trigger_status_start'] ?? ''),
            'trigger_status'       => sanitize_text_field($data['trigger_status'] ?? ''),
            'sort_order'           => (int) ($data['sort_order'] ?? 0),
        ]);
    }

    public function updateChecklist(int $id, array $data): bool {
        $fields = [];
        if (isset($data['name'])) $fields['name'] = sanitize_text_field($data['name']);
        if (isset($data['description'])) $fields['description'] = sanitize_textarea_field($data['description']);
        if (isset($data['trigger_status_start'])) $fields['trigger_status_start'] = sanitize_text_field($data['trigger_status_start']);
        if (isset($data['trigger_status'])) $fields['trigger_status'] = sanitize_text_field($data['trigger_status']);
        if (isset($data['sort_order'])) $fields['sort_order'] = (int) $data['sort_order'];
        if (empty($fields)) return false;
        return $this->MAIN->getDB()->update('checklists', $fields, ['id' => $id]) !== false;
    }

    public function deleteChecklist(int $id): bool {
        $db = $this->MAIN->getDB();
        $db->delete('checklist_items', ['checklist_id' => $id]);
        $db->delete('repair_type_checklists', ['checklist_id' => $id]);
        return $db->delete('checklists', ['id' => $id]) !== false;
    }

    public function getChecklist(int $id): ?array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('checklists');
        return $db->getRow("SELECT * FROM %i WHERE id = %d", $tbl, $id);
    }

    /**
     * Get all checklists (for management and dropdown).
     */
    public function getAllChecklists(): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('checklists');
        return $db->getResults("SELECT * FROM %i ORDER BY sort_order ASC, name ASC", $tbl);
    }

    public function getAllWithItems(): array {
        $cls = $this->getAllChecklists();
        foreach ($cls as &$cl) {
            $cl['items'] = $this->getItems((int) $cl['id']);
        }
        return $cls;
    }

    public function getChecklistsForType(int $repairTypeId): array {
        $ids = $this->getChecklistIdsForType($repairTypeId);
        $result = [];
        foreach ($ids as $id) {
            $cl = $this->getChecklist((int) $id);
            if ($cl) $result[] = ['id' => $cl['id'], 'name' => $cl['name']];
        }
        return $result;
    }

    // --- Repair Type ↔ Checklist assignment ---

    /**
     * Get checklist IDs assigned to a repair type.
     */
    public function getChecklistIdsForType(int $repairTypeId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('repair_type_checklists');
        return $db->getCol("SELECT checklist_id FROM %i WHERE repair_type_id = %d ORDER BY sort_order ASC", $tbl, $repairTypeId);
    }

    /**
     * Get full checklists (with items) assigned to a repair type.
     */
    public function getChecklistsByType(int $repairTypeId): array {
        $ids = $this->getChecklistIdsForType($repairTypeId);
        if (empty($ids)) return [];

        $result = [];
        foreach ($ids as $id) {
            $cl = $this->getChecklist((int) $id);
            if ($cl) {
                $cl['items'] = $this->getItems((int) $id);
                $result[] = $cl;
            }
        }
        return $result;
    }

    /**
     * Get repair types that use a checklist.
     */
    public function getRepairTypesForChecklist(int $checklistId): array {
        $db = $this->MAIN->getDB();
        $rtcTbl = $db->getTabelle('repair_type_checklists');
        $rtTbl = $db->getTabelle('repair_types');
        return $db->getResults(
            "SELECT rt.* FROM %i rtc JOIN %i rt ON rtc.repair_type_id = rt.id WHERE rtc.checklist_id = %d ORDER BY rt.name ASC",
            $rtcTbl, $rtTbl, $checklistId
        );
    }

    /**
     * Assign checklists to a repair type (replace all).
     */
    public function assignChecklistsToType(int $repairTypeId, array $checklistIds): void {
        $db = $this->MAIN->getDB();
        $db->delete('repair_type_checklists', ['repair_type_id' => $repairTypeId]);
        foreach ($checklistIds as $i => $clId) {
            $db->insert('repair_type_checklists', [
                'repair_type_id' => $repairTypeId,
                'checklist_id'   => (int) $clId,
                'sort_order'     => $i,
            ]);
        }
    }

    // --- Template Items ---

    public function addItem(int $checklistId, string $title, int $sortOrder = 0, array $extra = []): int {
        $fields = [
            'checklist_id' => $checklistId,
            'title'        => sanitize_text_field($title),
            'sort_order'   => $sortOrder,
        ];
        if (isset($extra['instructions'])) $fields['instructions'] = sanitize_textarea_field($extra['instructions']);
        if (isset($extra['default_duration'])) $fields['default_duration'] = (float) $extra['default_duration'];
        if (isset($extra['default_price'])) $fields['default_price'] = (float) $extra['default_price'];
        return $this->MAIN->getDB()->insert('checklist_items', $fields);
    }

    public function updateItem(int $id, array $data): bool {
        $fields = [];
        if (isset($data['title'])) $fields['title'] = sanitize_text_field($data['title']);
        if (isset($data['instructions'])) $fields['instructions'] = sanitize_textarea_field($data['instructions']);
        if (isset($data['default_duration'])) $fields['default_duration'] = (float) $data['default_duration'];
        if (isset($data['default_price'])) $fields['default_price'] = (float) $data['default_price'];
        if (isset($data['sort_order'])) $fields['sort_order'] = (int) $data['sort_order'];
        if (empty($fields)) return false;
        return $this->MAIN->getDB()->update('checklist_items', $fields, ['id' => $id]) !== false;
    }

    public function deleteItem(int $id): bool {
        return $this->MAIN->getDB()->delete('checklist_items', ['id' => $id]) !== false;
    }

    public function getItems(int $checklistId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('checklist_items');
        return $db->getResults("SELECT * FROM %i WHERE checklist_id = %d ORDER BY sort_order ASC", $tbl, $checklistId);
    }

    /**
     * Save items for a checklist.
     * Items with an 'id' are updated, items without are created.
     * Items not in the list are deleted (with their files).
     */
    public function saveItems(int $checklistId, array $items): void {
        $db = $this->MAIN->getDB();
        $existing = $this->getItems($checklistId);
        $existingIds = array_map(function($e) { return (int) $e['id']; }, $existing);

        $keepIds = [];
        foreach ($items as $i => $item) {
            $title = is_array($item) ? trim($item['title'] ?? '') : trim($item);
            if (empty($title)) continue;

            $fields = [
                'checklist_id'     => $checklistId,
                'title'            => sanitize_text_field($title),
                'instructions'     => is_array($item) ? sanitize_textarea_field($item['instructions'] ?? '') : '',
                'default_duration' => is_array($item) ? (float) ($item['default_duration'] ?? 0) : 0,
                'default_price'    => is_array($item) ? (float) ($item['default_price'] ?? 0) : 0,
                'sort_order'       => $i,
            ];

            $itemId = is_array($item) ? (int) ($item['id'] ?? 0) : 0;

            if ($itemId > 0 && in_array($itemId, $existingIds)) {
                // Update by ID
                $db->update('checklist_items', $fields, ['id' => $itemId]);
                $keepIds[] = $itemId;
            } else {
                // New item
                $keepIds[] = $db->insert('checklist_items', $fields);
            }
        }

        // Delete removed items + their files
        foreach ($existingIds as $oldId) {
            if (!in_array($oldId, $keepIds)) {
                $this->MAIN->getFileUpload()->deleteByEntity('checklist_item', $oldId);
                $db->delete('checklist_items', ['id' => $oldId]);
            }
        }
    }

    // --- Per-Repair Instances ---

    /**
     * Apply checklists from repair type to a repair.
     * Creates repair_checklist_items from the template.
     */
    public function applyToRepair(int $repairId, int $repairTypeId): int {
        $checklists = $this->getChecklistsByType($repairTypeId);
        $count = 0;

        foreach ($checklists as $cl) {
            $items = $this->getItems((int) $cl['id']);
            foreach ($items as $item) {
                $this->MAIN->getDB()->insert('repair_checklist_items', [
                    'repair_id'         => $repairId,
                    'checklist_id'      => (int) $cl['id'],
                    'checklist_item_id' => (int) $item['id'],
                    'group_name'        => $cl['name'],
                    'title'             => $item['title'],
                    'instructions'      => $item['instructions'] ?? '',
                    'default_duration'  => (float) ($item['default_duration'] ?? 0),
                    'default_price'     => (float) ($item['default_price'] ?? 0),
                    'sort_order'        => (int) $item['sort_order'],
                    'is_checked'        => 0,
                ]);
                $count++;
            }
        }

        return $count;
    }

    /**
     * Add a single step to a repair (manual, no template).
     */
    public function addRepairStep(int $repairId, string $groupName, string $title, int $sortOrder = 0): int {
        return $this->MAIN->getDB()->insert('repair_checklist_items', [
            'repair_id'  => $repairId,
            'group_name' => sanitize_text_field($groupName),
            'title'      => sanitize_text_field($title),
            'sort_order' => $sortOrder,
            'is_checked' => 0,
        ]);
    }

    /**
     * Get all checklist items for a repair (grouped by group_name).
     * Auto-syncs with template: reads live data from checklist_items,
     * adds new template steps, marks deleted ones as removed.
     */
    public function getRepairSteps(int $repairId): array {
        $db = $this->MAIN->getDB();
        $rciTbl = $db->getTabelle('repair_checklist_items');
        $ciTbl = $db->getTabelle('checklist_items');

        // Get existing repair step instances
        $instances = $db->getResults("SELECT * FROM %i WHERE repair_id = %d ORDER BY group_name ASC, sort_order ASC", $rciTbl, $repairId);

        // Get which checklists are linked to this repair (via repair_type or direct)
        $linkedChecklistIds = [];
        foreach ($instances as $inst) {
            if (!empty($inst['checklist_id']) && !in_array((int) $inst['checklist_id'], $linkedChecklistIds)) {
                $linkedChecklistIds[] = (int) $inst['checklist_id'];
            }
        }

        // Build map of existing instances by checklist_item_id
        $instanceMap = [];
        foreach ($instances as $inst) {
            $ciId = (int) ($inst['checklist_item_id'] ?? 0);
            if ($ciId > 0) {
                $instanceMap[$ciId] = $inst;
            }
        }

        // For each linked checklist, get current template items
        $result = [];
        foreach ($linkedChecklistIds as $clId) {
            $cl = $this->getChecklist($clId);
            if (!$cl) continue;
            $groupName = $cl['name'] ?: __('General', 'vollstart-repair-desk');
            $templateItems = $this->getItems($clId);

            $groupSteps = [];
            foreach ($templateItems as $ti) {
                $tiId = (int) $ti['id'];
                if (isset($instanceMap[$tiId])) {
                    // Existing instance — merge live template data with repair tracking data
                    $step = $instanceMap[$tiId];
                    $step['title'] = $ti['title']; // live from template
                    $step['instructions'] = $ti['instructions'] ?? '';
                    $step['default_duration'] = $ti['default_duration'];
                    $step['default_price'] = $ti['default_price'];
                    $step['_removed'] = false;
                    unset($instanceMap[$tiId]);
                } else {
                    // New template step — auto-add to repair
                    $newId = $db->insert('repair_checklist_items', [
                        'repair_id'         => $repairId,
                        'checklist_id'      => $clId,
                        'checklist_item_id' => $tiId,
                        'group_name'        => $groupName,
                        'title'             => $ti['title'],
                        'instructions'      => $ti['instructions'] ?? '',
                        'default_duration'  => (float) ($ti['default_duration'] ?? 0),
                        'default_price'     => (float) ($ti['default_price'] ?? 0),
                        'sort_order'        => (int) $ti['sort_order'],
                        'is_checked'        => 0,
                    ]);
                    $step = $db->getRow("SELECT * FROM %i WHERE id = %d", $rciTbl, $newId);
                    $step['_removed'] = false;
                }
                $groupSteps[] = $step;
            }

            $result[$groupName] = $groupSteps;
        }

        // Remaining instances not in any template = removed from template
        // Show as strikethrough (for billing/time tracking preservation)
        foreach ($instanceMap as $ciId => $orphan) {
            $group = $orphan['group_name'] ?: __('Removed', 'vollstart-repair-desk');
            $orphan['_removed'] = true;
            if (!isset($result[$group])) {
                $result[$group] = [];
            }
            $result[$group][] = $orphan;
        }

        // Also include manually added steps (checklist_item_id = 0 or NULL)
        $manualSteps = $db->getResults("SELECT * FROM %i WHERE repair_id = %d AND (checklist_item_id IS NULL OR checklist_item_id = 0) ORDER BY sort_order ASC", $rciTbl, $repairId);
        foreach ($manualSteps as $ms) {
            $group = $ms['group_name'] ?: __('General', 'vollstart-repair-desk');
            $ms['_removed'] = false;
            if (!isset($result[$group])) {
                $result[$group] = [];
            }
            $result[$group][] = $ms;
        }

        return $result;
    }

    /**
     * Get flat list of repair steps (for analytics).
     */
    public function getRepairStepsFlat(int $repairId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('repair_checklist_items');
        return $db->getResults("SELECT * FROM %i WHERE repair_id = %d ORDER BY sort_order ASC", $tbl, $repairId);
    }

    /**
     * Toggle a step checked/unchecked.
     */
    public function toggleStep(int $stepId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('repair_checklist_items');
        $step = $db->getRow("SELECT * FROM %i WHERE id = %d", $tbl, $stepId);
        if (!$step) return [];

        $isChecked = ($step['is_checked'] && $step['is_checked'] !== '0') ? 0 : 1;
        $fields = ['is_checked' => $isChecked];
        if ($isChecked) {
            $fields['checked_at'] = current_time('mysql');
            $fields['checked_by'] = wp_get_current_user()->display_name ?: 'System';
            // Auto-stop timer when checking off
            if (!empty($step['timer_started_at'])) {
                $started = strtotime($step['timer_started_at']);
                $elapsed = (time() - $started) / 60;
                $fields['tracked_duration'] = round((float) $step['tracked_duration'] + $elapsed, 2);
                $fields['timer_started_at'] = null;
            }
        } else {
            $fields['checked_at'] = null;
            $fields['checked_by'] = '';
        }

        $db->update('repair_checklist_items', $fields, ['id' => $stepId]);

        return $db->getRow("SELECT * FROM %i WHERE id = %d", $tbl, $stepId);
    }

    /**
     * Delete a step from a repair.
     */
    public function deleteRepairStep(int $stepId): bool {
        return $this->MAIN->getDB()->delete('repair_checklist_items', ['id' => $stepId]) !== false;
    }

    /**
     * Start/stop timer on a step.
     */
    public function toggleTimer(int $stepId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('repair_checklist_items');
        $step = $db->getRow("SELECT * FROM %i WHERE id = %d", $tbl, $stepId);
        if (!$step) return [];

        if (!empty($step['timer_started_at'])) {
            // Stop: calculate elapsed (UTC), add to tracked_duration
            $started = strtotime($step['timer_started_at'] . ' UTC');
            $elapsed = (time() - $started) / 60; // minutes
            $newDuration = max(0, (float) $step['tracked_duration'] + $elapsed);
            $db->update('repair_checklist_items', [
                'tracked_duration' => round($newDuration, 2),
                'timer_started_at' => null,
            ], ['id' => $stepId]);
            // Log to audit trail
            $repairId = (int) $step['repair_id'];
            if ($repairId > 0) {
                $this->MAIN->getRepairs()->logEvent(
                    $repairId,
                    'time_tracked',
                    '',
                    $step['title'] . ': ' . round($elapsed, 1) . ' min',
                    round((float) $step['tracked_duration'], 1) . ' min',
                    round($newDuration, 1) . ' min'
                );
            }
        } else {
            // Start: store UTC timestamp
            $db->update('repair_checklist_items', [
                'timer_started_at' => gmdate('Y-m-d H:i:s'),
            ], ['id' => $stepId]);
        }

        return $db->getRow("SELECT * FROM %i WHERE id = %d", $tbl, $stepId);
    }

    /**
     * Get totals for a repair: estimated + tracked duration, estimated + tracked price.
     */
    public function getTotals(int $repairId): array {
        $steps = $this->getRepairStepsFlat($repairId);
        $estDuration = 0;
        $estPrice = 0;
        $trackedDuration = 0;

        foreach ($steps as $s) {
            $estDuration += (float) ($s['default_duration'] ?? 0);
            $estPrice += (float) ($s['default_price'] ?? 0);
            $trackedDuration += (float) ($s['tracked_duration'] ?? 0);
        }

        return [
            'estimated_duration' => round($estDuration, 1),
            'estimated_price'    => round($estPrice, 2),
            'tracked_duration'   => round($trackedDuration, 2),
        ];
    }

    /**
     * Get progress for a repair (checked/total).
     */
    public function getProgress(int $repairId): array {
        $db = $this->MAIN->getDB();
        $tbl = $db->getTabelle('repair_checklist_items');
        $total = (int) $db->getVar("SELECT COUNT(*) FROM %i WHERE repair_id = %d", $tbl, $repairId);
        $checked = (int) $db->getVar("SELECT COUNT(*) FROM %i WHERE repair_id = %d AND is_checked = 1", $tbl, $repairId);
        return ['total' => $total, 'checked' => $checked, 'percent' => $total > 0 ? round(($checked / $total) * 100) : 0];
    }

    /**
     * Check if a step toggle should trigger a status change.
     * Checks per-checklist: start trigger on any check, done trigger when all steps of THAT checklist are done.
     * Returns the best (furthest forward) status slug, or null.
     */
    public function checkTriggers(int $repairId, array $step): ?string {
        if (empty($step['checklist_id'])) return null;

        $db = $this->MAIN->getDB();
        $clTbl = $db->getTabelle('checklists');
        $rciTbl = $db->getTabelle('repair_checklist_items');

        $cl = $db->getRow("SELECT trigger_status_start, trigger_status FROM %i WHERE id = %d", $clTbl, (int) $step['checklist_id']);
        if (!$cl) return null;

        $repair = $this->MAIN->getRepairs()->getById($repairId);
        if (!$repair) return null;

        $flowConfig = $this->MAIN->getRepairs()->getStatusFlowConfig();
        $statusOrder = [];
        foreach ($flowConfig as $i => $s) {
            $statusOrder[$s['slug']] = $i;
        }
        $currentPos = $statusOrder[$repair['status']] ?? -1;

        $bestStatus = null;
        $bestPos = $currentPos;

        $isChecked = !empty($step['is_checked']) && $step['is_checked'] !== '0';

        // Start trigger: any step checked in this checklist
        if ($isChecked && !empty($cl['trigger_status_start'])) {
            $pos = $statusOrder[$cl['trigger_status_start']] ?? -1;
            if ($pos > $bestPos) {
                $bestPos = $pos;
                $bestStatus = $cl['trigger_status_start'];
            }
        }

        // Done trigger: all steps of THIS checklist are checked
        if ($isChecked && !empty($cl['trigger_status'])) {
            $clId = (int) $step['checklist_id'];
            $total = (int) $db->getVar("SELECT COUNT(*) FROM %i WHERE repair_id = %d AND checklist_id = %d", $rciTbl, $repairId, $clId);
            $checked = (int) $db->getVar("SELECT COUNT(*) FROM %i WHERE repair_id = %d AND checklist_id = %d AND is_checked = 1", $rciTbl, $repairId, $clId);
            if ($total > 0 && $checked === $total) {
                $pos = $statusOrder[$cl['trigger_status']] ?? -1;
                if ($pos > $bestPos) {
                    $bestPos = $pos;
                    $bestStatus = $cl['trigger_status'];
                }
            }
        }

        return $bestStatus;
    }

    /**
     * Get trigger statuses for a repair.
     * Returns ['start' => slug|null, 'done' => slug|null].
     * Only returns statuses that are ahead of the current repair status.
     */
    public function getTriggerStatuses(int $repairId): array {
        $db = $this->MAIN->getDB();
        $rciTbl = $db->getTabelle('repair_checklist_items');
        $clTbl = $db->getTabelle('checklists');

        $rows = $db->getResults(
            "SELECT DISTINCT checklist_id FROM %i WHERE repair_id = %d AND checklist_id IS NOT NULL",
            $rciTbl,
            $repairId
        );

        $startStatuses = [];
        $doneStatuses = [];
        foreach ($rows as $r) {
            $cl = $db->getRow("SELECT trigger_status_start, trigger_status FROM %i WHERE id = %d", $clTbl, (int) $r['checklist_id']);
            if ($cl) {
                if (!empty($cl['trigger_status_start'])) $startStatuses[] = $cl['trigger_status_start'];
                if (!empty($cl['trigger_status'])) $doneStatuses[] = $cl['trigger_status'];
            }
        }

        $repair = $this->MAIN->getRepairs()->getById($repairId);
        if (!$repair) return ['start' => null, 'done' => null];

        $flowConfig = $this->MAIN->getRepairs()->getStatusFlowConfig();
        $statusOrder = [];
        foreach ($flowConfig as $i => $s) {
            $statusOrder[$s['slug']] = $i;
        }
        $currentPos = $statusOrder[$repair['status']] ?? -1;

        // Pick the furthest-ahead status (only forward)
        $pickBest = function(array $candidates) use ($statusOrder, $currentPos) {
            $best = null;
            $bestPos = $currentPos;
            foreach ($candidates as $ts) {
                $pos = $statusOrder[$ts] ?? -1;
                if ($pos > $bestPos) {
                    $bestPos = $pos;
                    $best = $ts;
                }
            }
            return $best;
        };

        return [
            'start' => $pickBest($startStatuses),
            'done'  => $pickBest($doneStatuses),
        ];
    }

}
