<?php
// ============================================================
// HumidityLabForm  — implements ILabForm
// Handles save/load/validate for the Temperature & Humidity Lab.
// Stores only THL-specific data; all equipment/company/request
// fields are read from the joined $request array at query time.
// ============================================================

require_once __DIR__ . '/../HumidityUncertaintyEngine.php';

class HumidityLabForm implements ILabForm {

    const MAX_SETPOINTS = 5;

    // ── Direct scalar fields stored in humidity_lab_forms ───
    private const SCALAR_FIELDS = [
        'certificate_number', 'calibration_date', 'instrument_type',
        'place_of_test', 'procedure', 'calibrated_by', 'checked_by', 'comments',
    ];

    private const DATE_FIELDS = ['date_of_issue', 'due_date'];

    private const NUMERIC_FIELDS = [
        'res_rh_eut', 'res_temp_eut', 'num_setpoints', 'num_of_measurements',
    ];

    public function getTableSchema(): string {
        return file_get_contents(__DIR__ . '/../../../database/humidity_schema.sql');
    }

    // ── Validation ───────────────────────────────────────────
    public function validate(array $post): array {
        $errors = [];
        if (empty(trim($post['certificate_number'] ?? ''))) {
            $errors[] = 'Certificate number is required.';
        }
        if (empty($post['date_of_issue'] ?? '')) {
            $errors[] = 'Date of issue is required.';
        }
        $num_sp = (int)($post['num_setpoints'] ?? 0);
        if ($num_sp < 1 || $num_sp > self::MAX_SETPOINTS) {
            $errors[] = 'Number of setpoints must be between 1 and ' . self::MAX_SETPOINTS . '.';
        }
        $num_m = (int)($post['num_of_measurements'] ?? 0);
        if ($num_m < 1) {
            $errors[] = 'Number of measurements per setpoint must be at least 1.';
        }
        return $errors;
    }

    // ── Save ─────────────────────────────────────────────────
    public function save(int $requestId, int $officerId, int $labId, array $post): int {
        $db     = Database::getInstance();
        $s      = fn($v) => htmlspecialchars(strip_tags(trim((string)$v)), ENT_QUOTES, 'UTF-8');
        $status = ($post['form_status'] ?? 'draft') === 'submitted' ? 'submitted' : 'draft';

        $num_sp = min((int)($post['num_setpoints'] ?? 3), self::MAX_SETPOINTS);
        $num_m  = max((int)($post['num_of_measurements'] ?? 5), 1);
        $res_rh   = (float)($post['res_rh_eut']   ?? 0.1);
        $res_temp = (float)($post['res_temp_eut']  ?? 0.1);

        // Build data array
        $data = ['form_status' => $status, 'num_setpoints' => $num_sp, 'num_of_measurements' => $num_m,
                 'res_rh_eut' => $res_rh, 'res_temp_eut' => $res_temp];

        foreach (self::SCALAR_FIELDS as $f) {
            $data[$f] = $s($post[$f] ?? '');
        }
        foreach (self::DATE_FIELDS as $f) {
            $raw = trim($post[$f] ?? '');
            $data[$f] = $raw ?: null;
        }

        // Process each setpoint
        for ($sp = 1; $sp <= self::MAX_SETPOINTS; $sp++) {
            $prefix = "sp{$sp}_";
            if ($sp > $num_sp) {
                // Clear unused setpoints
                foreach ($this->setpointColumns($sp) as $col) {
                    $data[$col] = null;
                }
                continue;
            }

            $data["{$prefix}setpoint_label"] = $s($post["{$prefix}setpoint_label"] ?? "SP{$sp}");

            $raw_temp_sp = trim($post["{$prefix}temp_setpoint"] ?? '');
            $data["{$prefix}temp_setpoint"] = $raw_temp_sp !== '' ? (float)$raw_temp_sp : null;
            $raw_rh_sp = trim($post["{$prefix}rh_setpoint"] ?? '');
            $data["{$prefix}rh_setpoint"]   = $raw_rh_sp   !== '' ? (float)$raw_rh_sp   : null;

            // Parse JSON arrays from hidden inputs (populated by JS)
            $ref_temps     = $this->parseFloatArray($post["{$prefix}ref_temps"]      ?? '[]');
            $ref_rh1       = $this->parseFloatArray($post["{$prefix}ref_rh1_readings"] ?? '[]');
            $ref_rh2       = $this->parseFloatArray($post["{$prefix}ref_rh2_readings"] ?? '[]');
            $eut_temps     = $this->parseFloatArray($post["{$prefix}eut_temps"]       ?? '[]');
            $eut_rhs       = $this->parseFloatArray($post["{$prefix}eut_rhs"]         ?? '[]');

            $corr_temp = (float)($post["{$prefix}correction_temp"] ?? 0);
            $corr_rh1  = (float)($post["{$prefix}correction_rh1"]  ?? 0);
            $corr_rh2  = (float)($post["{$prefix}correction_rh2"]  ?? 0);

            $data["{$prefix}ref_temps"]       = json_encode($ref_temps);
            $data["{$prefix}correction_temp"] = $corr_temp;
            $data["{$prefix}ref_rh1_readings"]= json_encode($ref_rh1);
            $data["{$prefix}correction_rh1"]  = $corr_rh1;
            $data["{$prefix}ref_rh2_readings"]= json_encode($ref_rh2);
            $data["{$prefix}correction_rh2"]  = $corr_rh2;
            $data["{$prefix}eut_temps"]        = json_encode($eut_temps);
            $data["{$prefix}eut_rhs"]          = json_encode($eut_rhs);

            // Run engine calculations if we have readings
            if (!empty($ref_temps) && !empty($eut_temps) && !empty($eut_rhs)) {
                $result = HumidityUncertaintyEngine::processSetpoint(
                    $ref_temps, $corr_temp,
                    !empty($ref_rh1) ? $ref_rh1 : [0],
                    $corr_rh1,
                    !empty($ref_rh2) ? $ref_rh2 : [0],
                    $corr_rh2,
                    $eut_temps, $eut_rhs,
                    $res_rh, $res_temp, $sp
                );
                $data["{$prefix}avg_ref_temp"]       = $result['avg_ref_temp'];
                $data["{$prefix}corrected_ref_temp"]  = $result['corrected_ref_temp'];
                $data["{$prefix}corrected_ref_rh"]    = $result['corrected_ref_rh'];
                $data["{$prefix}avg_eut_temp"]         = $result['avg_eut_temp'];
                $data["{$prefix}std_eut_temp"]         = $result['std_eut_temp'];
                $data["{$prefix}avg_eut_rh"]           = $result['avg_eut_rh'];
                $data["{$prefix}std_eut_rh"]           = $result['std_eut_rh'];
                $data["{$prefix}temp_deviation"]       = $result['temp_deviation'];
                $data["{$prefix}rh_deviation"]         = $result['rh_deviation'];
                $data["{$prefix}temp_uncertainty"]     = $result['temp_uncertainty'];
                $data["{$prefix}rh_uncertainty"]       = $result['rh_uncertainty'];
                $data["{$prefix}include_temp"]         = $result['include_temp'] ? 1 : 0;
            }
        }

        $existing = $this->load($requestId);
        if ($existing) {
            $sets   = implode(', ', array_map(fn($k) => "`$k` = ?", array_keys($data)));
            $vals   = array_values($data);
            $vals[] = $existing['form_id'];
            $db->prepare("UPDATE humidity_lab_forms SET $sets WHERE form_id = ?")->execute($vals);
            return $existing['form_id'];
        }

        $data['request_id'] = $requestId;
        $data['officer_id'] = $officerId;
        $data['lab_id']     = $labId;
        $cols   = implode(', ', array_map(fn($k) => "`$k`", array_keys($data)));
        $places = implode(', ', array_fill(0, count($data), '?'));
        $db->prepare("INSERT INTO humidity_lab_forms ($cols) VALUES ($places)")->execute(array_values($data));
        return (int)$db->lastInsertId();
    }

    // ── Load ─────────────────────────────────────────────────
    public function load(int $requestId): ?array {
        $db   = Database::getInstance();
        $stmt = $db->prepare('SELECT * FROM humidity_lab_forms WHERE request_id = ?');
        $stmt->execute([$requestId]);
        return $stmt->fetch() ?: null;
    }

    public function isSubmitted(array $formData): bool {
        return ($formData['form_status'] ?? '') === 'submitted';
    }

    // ── Helper: parse JSON float array from POST ──────────────
    private function parseFloatArray(string $json): array {
        $arr = json_decode($json, true);
        if (!is_array($arr)) return [];
        return array_map('floatval', array_filter($arr, fn($v) => $v !== '' && $v !== null));
    }

    // ── Helper: column names for one setpoint ────────────────
    private function setpointColumns(int $sp): array {
        $p = "sp{$sp}_";
        return [
            "{$p}setpoint_label", "{$p}temp_setpoint", "{$p}rh_setpoint",
            "{$p}ref_temps", "{$p}correction_temp",
            "{$p}ref_rh1_readings", "{$p}correction_rh1",
            "{$p}ref_rh2_readings", "{$p}correction_rh2",
            "{$p}eut_temps", "{$p}eut_rhs",
            "{$p}avg_ref_temp", "{$p}corrected_ref_temp", "{$p}corrected_ref_rh",
            "{$p}avg_eut_temp", "{$p}std_eut_temp",
            "{$p}avg_eut_rh", "{$p}std_eut_rh",
            "{$p}temp_deviation", "{$p}rh_deviation",
            "{$p}temp_uncertainty", "{$p}rh_uncertainty", "{$p}include_temp",
        ];
    }

    // ── Public helper: build result array for display ─────────
    public static function buildResults(array $formData): array {
        $results = [];
        $num_sp  = (int)($formData['num_setpoints'] ?? 0);
        for ($sp = 1; $sp <= $num_sp; $sp++) {
            $p = "sp{$sp}_";
            $results[] = [
                'setpoint'           => $formData["{$p}setpoint_label"] ?? "SP{$sp}",
                'corrected_ref_temp' => $formData["{$p}corrected_ref_temp"],
                'corrected_ref_rh'   => $formData["{$p}corrected_ref_rh"],
                'avg_eut_temp'       => $formData["{$p}avg_eut_temp"],
                'avg_eut_rh'         => $formData["{$p}avg_eut_rh"],
                'temp_deviation'     => $formData["{$p}temp_deviation"],
                'rh_deviation'       => $formData["{$p}rh_deviation"],
                'temp_uncertainty'   => $formData["{$p}temp_uncertainty"],
                'rh_uncertainty'     => $formData["{$p}rh_uncertainty"],
                'include_temp'       => (bool)($formData["{$p}include_temp"] ?? false),
                'ref_temps'          => json_decode($formData["{$p}ref_temps"] ?? '[]', true),
                'eut_temps'          => json_decode($formData["{$p}eut_temps"] ?? '[]', true),
                'eut_rhs'            => json_decode($formData["{$p}eut_rhs"]   ?? '[]', true),
            ];
        }
        return $results;
    }
}
