<?php
/**
 * سودانا — FlowEngine v2 (PATCHED)
 * PATCH: execution_id propagation + INIT/RUNNING/SUCCESS/FAILED/ROLLED_BACK states
 *        + BillingEngine reserve→commit→rollback integration
 *        + ToolsEngine via try/catch with structured errors
 *        + ALL paths return structured result (no silent failure)
 */
class FlowEngine {

    // Execution states
    const STATE_INIT        = 'INIT';
    const STATE_RESERVED    = 'RESERVED';
    const STATE_RUNNING     = 'RUNNING';
    const STATE_SUCCESS     = 'SUCCESS';
    const STATE_FAILED      = 'FAILED';
    const STATE_ROLLED_BACK = 'ROLLED_BACK';

    // Flow registry
    const FLOW_MAP = [
        'medical'     => ['flow_id'=>'medical_telemedicine_v1',   'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'family'      => ['flow_id'=>'medical_telemedicine_v1',   'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'pharmacy'    => ['flow_id'=>'medical_telemedicine_v1',   'steps'=>3,'requires_provider'=>true, 'billing_tier'=>'standard'],
        'emergency'   => ['flow_id'=>'medical_telemedicine_v1',   'steps'=>2,'requires_provider'=>true, 'billing_tier'=>'micro'],
        'legal'       => ['flow_id'=>'legal_consultation_v1',     'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'business'    => ['flow_id'=>'business_consulting_v1',    'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'bde'         => ['flow_id'=>'business_consulting_v1',    'steps'=>5,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'engineering' => ['flow_id'=>'engineering_consulting_v1', 'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'premium'],
        'agriculture' => ['flow_id'=>'agriculture_consulting_v1', 'steps'=>4,'requires_provider'=>true, 'billing_tier'=>'standard'],
        'education'   => ['flow_id'=>'service_booking_v1',        'steps'=>3,'requires_provider'=>true, 'billing_tier'=>'standard'],
        'career'      => ['flow_id'=>'service_booking_v1',        'steps'=>3,'requires_provider'=>false,'billing_tier'=>'micro'],
        'finance'     => ['flow_id'=>'service_booking_v1',        'steps'=>2,'requires_provider'=>false,'billing_tier'=>'free'],
        'tech'        => ['flow_id'=>'service_booking_v1',        'steps'=>3,'requires_provider'=>false,'billing_tier'=>'standard'],
        'mental'      => ['flow_id'=>'service_booking_v1',        'steps'=>3,'requires_provider'=>true, 'billing_tier'=>'micro'],
        'family'      => ['flow_id'=>'family_support_v1',        'steps'=>3,'requires_provider'=>true, 'billing_tier'=>'micro'],
        'community'   => ['flow_id'=>'service_booking_v1',        'steps'=>2,'requires_provider'=>false,'billing_tier'=>'free'],
        'market'      => ['flow_id'=>'service_booking_v1',        'steps'=>2,'requires_provider'=>false,'billing_tier'=>'free'],
        'enterprise'  => ['flow_id'=>'corporate_solutions_v1',    'steps'=>5,'requires_provider'=>true, 'billing_tier'=>'enterprise'],
        'notifications'=>['flow_id'=>'service_booking_v1',        'steps'=>1,'requires_provider'=>false,'billing_tier'=>'free'],
    ];

    const OUTPUT_MAP = [
        'ai_answer'       => ['type'=>'text',     'tool'=>null],
        'ai_document'     => ['type'=>'document',  'tool'=>'document'],
        'ai_code'         => ['type'=>'code',      'tool'=>'code'],
        'provider_booking'=> ['type'=>'booking',   'tool'=>null],
        'ai_analysis'     => ['type'=>'analysis',  'tool'=>'analysis'],
    ];

    /**
     * MAIN ENTRY — with execution_id lifecycle
     * Implements: INIT → RESERVED → RUNNING → SUCCESS/FAILED → ROLLED_BACK
     */
    public static function run(string $sector, string $query,
            int $userId = 0, array $ctx = []): array {

        // ── [1] INIT ──────────────────────────────────────────────────
        $executionId = $ctx['execution_id'] ?? uniqid('exec_', true);
        $traceId     = $executionId;
        self::logState($executionId, self::STATE_INIT, $sector, $userId, $query);

        require_once __DIR__ . '/BillingEngine.php';

        $flowCfg = self::FLOW_MAP[$sector] ?? self::dynamicFlow($sector, $query);
        $intent  = self::classifyIntent($query, $sector, $flowCfg);
        $cost    = BillingEngine::TIERS[BillingEngine::SECTOR_TIER[$sector] ?? 'free'];

        // ── [2] BILLING RESERVE ──────────────────────────────────────
        if ($cost > 0 && $userId > 0) {
            $billing = BillingEngine::reserveFunds($userId, $sector, $cost, $executionId);
            if ($billing['decision'] === 'deny') {
                self::logState($executionId, self::STATE_FAILED, $sector, $userId, 'billing_denied');
                return [
                    'status'       => 'billing_required',
                    'action'       => 'show_payment',
                    'billing'      => $billing,
                    'execution_id' => $executionId,
                    'trace_id'     => $traceId,
                ];
            }
            if ($billing['decision'] === 'create_invoice') {
                self::logState($executionId, 'INVOICE_PENDING', $sector, $userId, 'invoice_created');
                return [
                    'status'      => 'invoice_created',
                    'action'      => 'redirect_payment',
                    'payment_url' => $billing['payment_url'] ?? '',
                    'invoice_id'  => $billing['invoice_id'] ?? null,
                    'execution_id'=> $executionId,
                    'trace_id'    => $traceId,
                ];
            }
            self::logState($executionId, self::STATE_RESERVED, $sector, $userId, 'funds_reserved');
        } else {
            $billing = ['decision'=>'allow','cost'=>0,'tier'=>'free'];
        }

        // ── [3] EXECUTE ───────────────────────────────────────────────
        self::logState($executionId, self::STATE_RUNNING, $sector, $userId, $intent['execution_mode']);

        try {
            $result = match($intent['execution_mode']) {
                'provider_booking' => self::executeProviderFlow($flowCfg, $query, $userId, $ctx, $executionId),
                'ai_document'      => self::executeAIDocument($query, $sector, $userId, $ctx, $executionId),
                'ai_code'          => self::executeAICode($query, $sector, $userId, $ctx, $executionId),
                'ai_analysis'      => self::executeAIAnalysis($query, $sector, $userId, $ctx, $executionId),
                default            => self::executeAIAnswer($query, $sector, $userId, $ctx, $executionId),
            };

            // ── [4a] SUCCESS ─────────────────────────────────────────
            self::logState($executionId, self::STATE_SUCCESS, $sector, $userId, $intent['execution_mode']);
            if ($cost > 0) BillingEngine::commitFunds($executionId);

            return array_merge($result, [
                'execution_id' => $executionId,
                'execution_state' => self::STATE_SUCCESS,
                'billing'      => $billing,
                'intent'       => $intent,
                'trace_id'     => $traceId,
            ]);

        } catch (Throwable $e) {
            // ── [4b] FAILED → ROLLBACK ───────────────────────────────
            self::logState($executionId, self::STATE_FAILED, $sector, $userId, $e->getMessage());
            if ($cost > 0) BillingEngine::rollbackFunds($executionId);
            self::logState($executionId, self::STATE_ROLLED_BACK, $sector, $userId, 'auto_rollback');
            error_log("[FlowEngine] execution_id=$executionId sector=$sector error=" . $e->getMessage());

            // Structured error response (no silent failure)
            return [
                'status'          => 'execution_failed',
                'action'          => 'show_error',
                'execution_id'    => $executionId,
                'execution_state' => self::STATE_ROLLED_BACK,
                'billing_refunded'=> $cost > 0,
                'error'           => $e->getMessage(),
                'message'         => 'حدث خطأ أثناء التنفيذ. تم إلغاء الحجز وإعادة رصيدك.',
                'trace_id'        => $traceId,
            ];
        }
    }

    /**
     * Classify intent from query + sector config
     */
    public static function classifyIntent(string $query, string $sector, array $flowCfg): array {
        $q = mb_strtolower($query);
        $docKeywords  = ['وثيقة','مستند','عقد','تقرير','pdf','doc','contract','report','document','اكتب','أنشئ'];
        $codeKeywords = ['كود','برمجة','code','script','function','php','python','javascript','html'];
        $bookKeywords = ['احجز','موعد','استشارة','book','appointment','schedule','consultation'];

        $isDoc  = array_filter($docKeywords,  fn($k) => str_contains($q, mb_strtolower($k)));
        $isCode = array_filter($codeKeywords, fn($k) => str_contains($q, mb_strtolower($k)));
        $isBook = array_filter($bookKeywords, fn($k) => str_contains($q, mb_strtolower($k)));

        $requiresProvider = $flowCfg['requires_provider'] ?? false;
        $executionMode = 'ai_answer';
        if ($requiresProvider || !empty($isBook))  $executionMode = 'provider_booking';
        if (!empty($isDoc))  $executionMode = 'ai_document';
        if (!empty($isCode)) $executionMode = 'ai_code';

        $tools = ['ai_chat'];
        if ($executionMode === 'ai_document')  $tools = ['ai_chat','pdf_gen'];
        if ($executionMode === 'ai_code')      $tools = ['ai_chat','code_gen'];
        if ($executionMode === 'ai_analysis')  $tools = ['ai_chat','analysis'];
        if ($requiresProvider)                 $tools[] = 'provider_match';

        require_once __DIR__ . '/BillingEngine.php';
        $billingInfo = BillingEngine::getBillingClass($sector);

        return [
            'intent'           => $executionMode,
            'domain'           => $sector,
            'output_type'      => self::OUTPUT_MAP[$executionMode]['type'] ?? 'text',
            'tools'            => $tools,
            'complexity'       => mb_strlen($query) > 100 ? 'high' : 'standard',
            'billing_class'    => $billingInfo['tier'],
            'cost_sdg'         => $billingInfo['cost_sdg'],
            'requires_provider'=> $requiresProvider || $executionMode === 'provider_booking',
            'flow_type'        => $executionMode,
            'execution_mode'   => $executionMode,
        ];
    }

    public static function getAvailableFlows(): array {
        return array_map(fn($s,$c) => ['sector'=>$s,'flow_id'=>$c['flow_id'],'steps'=>$c['steps'],'billing_tier'=>$c['billing_tier']],
            array_keys(self::FLOW_MAP), self::FLOW_MAP);
    }

    // ── Execution handlers ─────────────────────────────────────────────

    private static function executeProviderFlow(array $cfg, string $query, int $userId, array $ctx, string $exId): array {
        require_once __DIR__ . '/StepController.php';
        $flowId  = $cfg['flow_id'];
        $session = StepController::start($flowId, $userId, array_merge($ctx, ['query'=>$query,'execution_id'=>$exId]));
        if (!$session) throw new RuntimeException("StepController::start() returned null for flow: $flowId");
        return [
            'status'      => 'flow_started',
            'action'      => 'show_flow',
            'flow_id'     => $flowId,
            'session_id'  => $session['session_id'] ?? null,
            'steps_total' => $cfg['steps'],
            'ui_url'      => (defined('SITE_URL')?SITE_URL:'') . '/app/flow.php?flow_id=' . urlencode($flowId) . '&exec=' . $exId,
            'query'       => $query,
        ];
    }

    private static function executeAIAnswer(string $query, string $sector, int $userId, array $ctx, string $exId): array {
        return [
            'status'      => 'ai_answered',
            'action'      => 'display_answer',
            'output_type' => 'text',
            'via_gateway' => true,
            'sector'      => $sector,
            'query'       => $query,
            'execution_id'=> $exId,
        ];
    }

    private static function executeAIDocument(string $query, string $sector, int $userId, array $ctx, string $exId): array {
        require_once __DIR__ . '/ToolsEngine.php';
        $result = ToolsEngine::run('document', $query, ['sector'=>$sector,'user_id'=>$userId,'execution_id'=>$exId]);
        if (!($result['ok'] ?? false)) throw new RuntimeException($result['error'] ?? 'document_generation_failed');
        return ['status'=>'document_generated','action'=>'show_document','output_type'=>'document','document'=>$result,'execution_id'=>$exId];
    }

    private static function executeAICode(string $query, string $sector, int $userId, array $ctx, string $exId): array {
        require_once __DIR__ . '/ToolsEngine.php';
        $result = ToolsEngine::run('code', $query, ['sector'=>$sector,'user_id'=>$userId,'execution_id'=>$exId]);
        if (!($result['ok'] ?? false)) throw new RuntimeException($result['error'] ?? 'code_generation_failed');
        return ['status'=>'code_generated','action'=>'show_code','output_type'=>'code','code'=>$result,'execution_id'=>$exId];
    }

    private static function executeAIAnalysis(string $query, string $sector, int $userId, array $ctx, string $exId): array {
        require_once __DIR__ . '/ToolsEngine.php';
        $result = ToolsEngine::run('analysis', $query, ['sector'=>$sector,'user_id'=>$userId,'execution_id'=>$exId]);
        if (!($result['ok'] ?? false)) throw new RuntimeException($result['error'] ?? 'analysis_failed');
        return ['status'=>'analysis_done','action'=>'show_analysis','output_type'=>'analysis','analysis'=>$result,'execution_id'=>$exId];
    }

    private static function dynamicFlow(string $sector, string $query): array {
        return ['flow_id'=>'service_booking_v1','steps'=>3,'requires_provider'=>false,'billing_tier'=>'free','dynamic'=>true];
    }

    // ── State logger ─────────────────────────────────────────────────
    private static function logState(string $executionId, string $state,
            string $sector, int $userId, string $detail = ''): void {
        try {
            db()->prepare(
                "INSERT INTO audit_logs (action,entity_type,entity_id,data,created_at)
                 VALUES (?,?,?,?,NOW())"
            )->execute([
                "execution.$state",
                'execution',
                0,
                json_encode(['execution_id'=>$executionId,'sector'=>$sector,
                             'user_id'=>$userId,'detail'=>$detail], JSON_UNESCAPED_UNICODE)
            ]);
        } catch (Throwable $e) {
            error_log("[FlowEngine::log] execution_id=$executionId state=$state error=" . $e->getMessage());
        }
    }
}
