<?php
declare(strict_types=1);

function workflowStart(PDO $pdo, string $module, int $recordId, ?int $templateId = null, int $userId = 0): int
{
    if ($templateId === null || $templateId <= 0) {
        $tmplStmt = $pdo->prepare("SELECT id FROM workflow_templates WHERE module = ? AND is_active = 1 ORDER BY id LIMIT 1");
        $tmplStmt->execute([$module]);
        $templateId = (int)$tmplStmt->fetchColumn();
    }
    if ($templateId <= 0) throw new RuntimeException('No active workflow template found for module: ' . $module);

    $stepsStmt = $pdo->prepare("SELECT * FROM workflow_template_steps WHERE template_id = ? ORDER BY step_order");
    $stepsStmt->execute([$templateId]);
    $steps = $stepsStmt->fetchAll();
    if (empty($steps)) throw new RuntimeException('Template has no steps');

    $pdo->prepare("INSERT INTO workflow_runs (template_id, module, record_id, status, created_by, current_step_order) VALUES (?, ?, ?, 'pending', ?, 1)")
        ->execute([$templateId, $module, $recordId, $userId]);
    $runId = (int)$pdo->lastInsertId();

    $ins = $pdo->prepare("INSERT INTO workflow_run_steps (run_id, template_step_id, step_order, label, role_id, assigned_user_id, status) VALUES (?, ?, ?, ?, ?, ?, 'pending')");
    foreach ($steps as $s) {
        $assigned = workflowFindApprover($pdo, (int)$s['role_id']);
        $ins->execute([$runId, (int)$s['id'], (int)$s['step_order'], $s['label'], (int)$s['role_id'], $assigned]);
    }

    workflowNotifyFirstStep($pdo, $runId, $userId);
    return $runId;
}

function workflowFindApprover(PDO $pdo, int $roleId): ?int
{
    $stmt = $pdo->prepare("SELECT id FROM users WHERE role_id = ? AND is_active = 1 AND deleted_at IS NULL ORDER BY id LIMIT 1");
    $stmt->execute([$roleId]);
    $id = $stmt->fetchColumn();
    return $id ? (int)$id : null;
}

function workflowNotifyFirstStep(PDO $pdo, int $runId, int $creatorId): void
{
    $stmt = $pdo->prepare("SELECT rs.id, rs.assigned_user_id FROM workflow_run_steps rs WHERE rs.run_id = ? AND rs.step_order = 1");
    $stmt->execute([$runId]);
    $first = $stmt->fetch();
    if ($first && $first['assigned_user_id']) {
        notify($pdo, (int)$first['assigned_user_id'], 'Approval Required', 'A new request requires your approval', 'workflow', 'workflow_approvals.php', $runId);
    }
}

function workflowApprove(PDO $pdo, int $stepId, int $userId, string $comment = ''): array
{
    $step = $pdo->prepare("SELECT rs.*, r.module, r.record_id, r.id AS run_id FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE rs.id = ?");
    $step->execute([$stepId]);
    $step = $step->fetch();
    if (!$step) throw new RuntimeException('Step not found');
    if ($step['status'] !== 'pending') throw new RuntimeException('Step already ' . $step['status']);
    if ((int)$step['assigned_user_id'] !== $userId && (int)$step['delegate_user_id'] !== $userId && !isAdmin())
        throw new RuntimeException('Not assigned to you');

    $pdo->beginTransaction();
    try {
        $pdo->prepare("UPDATE workflow_run_steps SET status = 'approved', action_by = ?, comment = ?, acted_at = NOW() WHERE id = ?")
            ->execute([$userId, $comment, $stepId]);

        $nextStep = $pdo->prepare("SELECT rs.id, rs.step_order FROM workflow_run_steps rs WHERE rs.run_id = ? AND rs.step_order > ? AND rs.status = 'pending' ORDER BY rs.step_order LIMIT 1");
        $nextStep->execute([$step['run_id'], $step['step_order']]);
        $next = $nextStep->fetch();

        if ($next) {
            $pdo->prepare("UPDATE workflow_runs SET current_step_order = ? WHERE id = ?")->execute([$next['step_order'], $step['run_id']]);
            workflowNotifyStepApprover($pdo, $next['id'], $step['run_id'], $userId);
            $pdo->commit();
            return ['status' => 'step_approved', 'next_step_id' => (int)$next['id']];
        } else {
            $pdo->prepare("UPDATE workflow_runs SET status = 'approved', current_step_order = NULL WHERE id = ?")->execute([$step['run_id']]);
            $pdo->commit();
            workflowHandleComplete($pdo, $step['module'], $step['record_id'], $step['run_id'], $userId);
            return ['status' => 'workflow_completed'];
        }
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
}

function workflowReject(PDO $pdo, int $stepId, int $userId, string $comment): void
{
    $step = $pdo->prepare("SELECT rs.*, r.module, r.record_id, r.id AS run_id FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE rs.id = ?");
    $step->execute([$stepId]);
    $step = $step->fetch();
    if (!$step) throw new RuntimeException('Step not found');
    if ($step['status'] !== 'pending') throw new RuntimeException('Step already ' . $step['status']);
    if ((int)$step['assigned_user_id'] !== $userId && (int)$step['delegate_user_id'] !== $userId && !isAdmin())
        throw new RuntimeException('Not assigned to you');

    $pdo->beginTransaction();
    try {
        $pdo->prepare("UPDATE workflow_run_steps SET status = 'rejected', action_by = ?, comment = ?, acted_at = NOW() WHERE id = ?")
            ->execute([$userId, $comment, $stepId]);
        $pdo->prepare("UPDATE workflow_runs SET status = 'rejected', current_step_order = NULL WHERE id = ?")->execute([$step['run_id']]);
        $pdo->commit();
        workflowHandleRejected($pdo, $step['module'], $step['record_id'], $step['run_id'], $comment, $userId);
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
}

function workflowReturn(PDO $pdo, int $stepId, int $userId, string $comment): void
{
    $step = $pdo->prepare("SELECT rs.*, r.module, r.record_id, r.id AS run_id FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE rs.id = ?");
    $step->execute([$stepId]);
    $step = $step->fetch();
    if (!$step) throw new RuntimeException('Step not found');
    if ($step['status'] !== 'pending') throw new RuntimeException('Step already ' . $step['status']);
    if ((int)$step['assigned_user_id'] !== $userId && (int)$step['delegate_user_id'] !== $userId && !isAdmin())
        throw new RuntimeException('Not assigned to you');

    $pdo->beginTransaction();
    try {
        $pdo->prepare("UPDATE workflow_run_steps SET status = 'returned', action_by = ?, comment = ?, acted_at = NOW() WHERE id = ?")
            ->execute([$userId, $comment, $stepId]);
        $pdo->prepare("UPDATE workflow_runs SET status = 'returned', current_step_order = ? WHERE id = ?")->execute([$step['step_order'], $step['run_id']]);
        $pdo->commit();
        workflowHandleReturned($pdo, $step['module'], $step['record_id'], $step['run_id'], $comment, $userId);
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
}

function workflowNotifyStepApprover(PDO $pdo, int $stepId, int $runId, int $actionBy): void
{
    $stmt = $pdo->prepare("SELECT assigned_user_id, delegate_user_id FROM workflow_run_steps WHERE id = ?");
    $stmt->execute([$stepId]);
    $step = $stmt->fetch();
    if ($step) {
        $target = (int)($step['delegate_user_id'] ?: $step['assigned_user_id']);
        if ($target) {
            notify($pdo, $target, 'Approval Required', 'A request is pending your approval', 'workflow', 'workflow_approvals.php', $runId);
        }
    }
}

function workflowGetCreator(PDO $pdo, string $module, int $recordId): ?int
{
    $cfg = getModuleConfig($pdo, $module);
    if (!$cfg) return null;
    $stmt = $pdo->prepare("SELECT {$cfg['creator_column']} FROM {$cfg['table_name']} WHERE id = ?");
    $stmt->execute([$recordId]);
    $id = $stmt->fetchColumn();
    return $id ? (int)$id : null;
}

function workflowHandleComplete(PDO $pdo, string $module, int $recordId, int $runId, int $actionBy): void
{
    $creatorId = workflowGetCreator($pdo, $module, $recordId);
    if ($creatorId) {
        notify($pdo, $creatorId, 'Request Approved', 'Your request has been fully approved', 'workflow', 'workflow_approvals.php', $runId);
    }
    $cfg = getModuleConfig($pdo, $module);
    if ($cfg) {
        $pdo->prepare("UPDATE {$cfg['table_name']} SET {$cfg['status_column']} = ? WHERE id = ?")->execute([$cfg['approved_value'], $recordId]);
    }
}

function workflowHandleRejected(PDO $pdo, string $module, int $recordId, int $runId, string $comment, int $actionBy): void
{
    $creatorId = workflowGetCreator($pdo, $module, $recordId);
    if ($creatorId) {
        notify($pdo, $creatorId, 'Request Rejected', 'Your request was rejected: ' . $comment, 'workflow', 'workflow_approvals.php', $runId);
    }
    $cfg = getModuleConfig($pdo, $module);
    if ($cfg) {
        $pdo->prepare("UPDATE {$cfg['table_name']} SET {$cfg['status_column']} = ? WHERE id = ?")->execute([$cfg['rejected_value'], $recordId]);
    }
}

function workflowHandleReturned(PDO $pdo, string $module, int $recordId, int $runId, string $comment, int $actionBy): void
{
    $creatorId = workflowGetCreator($pdo, $module, $recordId);
    if ($creatorId) {
        notify($pdo, $creatorId, 'Request Returned', 'Your request needs revision: ' . $comment, 'workflow', 'workflow_approvals.php', $runId);
    }
    $cfg = getModuleConfig($pdo, $module);
    if ($cfg) {
        $pdo->prepare("UPDATE {$cfg['table_name']} SET {$cfg['status_column']} = ? WHERE id = ?")->execute([$cfg['returned_value'], $recordId]);
    }
}

function getModuleConfig(PDO $pdo, string $module): ?array
{
    $stmt = $pdo->prepare("SELECT * FROM workflow_modules WHERE module = ? AND is_active = 1 LIMIT 1");
    $stmt->execute([$module]);
    $cfg = $stmt->fetch();
    return $cfg ?: null;
}
