<?php
require_once __DIR__ . '/utils.php';

function parse_key_value_from_rest(string $rest, string $key): string {
    if (preg_match('/' . preg_quote($key, '/') . '=([^ ]+)/', $rest, $x)) {
        return trim($x[1]);
    }
    return '';
}

function is_meaningful_zip_source(?string $source): bool {
    $source = strtoupper(trim((string)$source));
    return $source !== '' && $source !== 'UNKNOWN' && $source !== '—' && $source !== '-';
}

function prefer_zip_source(string $current, string $candidate): string {
    $current = trim($current);
    $candidate = trim($candidate);

    if (!is_meaningful_zip_source($candidate)) {
        return $current;
    }

    if (!is_meaningful_zip_source($current)) {
        return $candidate;
    }

    $priority = [
        'TWILIO_RETRY_SUCCESS' => 100,
        'TWILIO_RETRY_FAILED' => 90,
        'TWILIO_RETRY_HTTP_FAILED' => 85,
        'TWILIO_RETRY' => 80,
        'TWILIO_FALLBACK' => 70,
        'TWILIO' => 60,
        'CACHED' => 50,
        'IVR' => 40,
    ];

    $currentScore = $priority[strtoupper($current)] ?? 10;
    $candidateScore = $priority[strtoupper($candidate)] ?? 10;

    return ($candidateScore >= $currentScore) ? $candidate : $current;
}

function prefer_value(string $current, string $candidate): string {
    $current = trim($current);
    $candidate = trim($candidate);

    if ($candidate === '' || $candidate === '—' || $candidate === '-') {
        return $current;
    }

    if ($current === '' || $current === '—' || $current === '-') {
        return $candidate;
    }

    return $current;
}

function parse_sd_line(string $line): ?array {
    $line = trim($line);
    if ($line === '') return null;

    if (!preg_match('/^\[(.*?)\]\s+([A-Z_]+)\s+(.*)$/', $line, $m)) {
        return null;
    }

    $ts    = trim($m[1]);
    $event = trim($m[2]);
    $rest  = trim($m[3]);

    $row = [
        'ts' => $ts,
        'event' => $event,
        'callSid' => '',
        'caller_id' => '',
        'zip_code' => '',
        'service_category' => '',
        'bid' => '',
        'buyer_id' => '',
        'request_id' => '',
        'endpoint_http' => '',
        'dial_to' => '',
        'zip_source' => '',
        'twilio_zip' => '',
        'retry_zip' => '',
        'original_zip' => '',
        'raw' => $line,
    ];

    if (preg_match('/callSid=([A-Za-z0-9]+)/', $rest, $x)) {
        $row['callSid'] = $x[1];
    }

    if (preg_match('/request_id=([0-9]+)/', $rest, $x)) {
        $row['request_id'] = $x[1];
    }

    if (preg_match('/http=([0-9]+)/', $rest, $x)) {
        $row['endpoint_http'] = $x[1];
    }

    if (preg_match('/zip=([0-9]{5})/', $rest, $x)) {
        $row['zip_code'] = $x[1];
    }

    if (preg_match('/retry_zip=([0-9]{5})/', $rest, $x)) {
        $row['retry_zip'] = $x[1];
    }

    if (preg_match('/original_zip=([0-9]{5})/', $rest, $x)) {
        $row['original_zip'] = $x[1];
    }

    $row['zip_source'] = parse_key_value_from_rest($rest, 'zip_source');
    $row['twilio_zip'] = parse_key_value_from_rest($rest, 'twilio_zip');

    $payloadPos = strpos($rest, 'payload=');
    $respPos    = strpos($rest, ' resp=');

    if ($payloadPos !== false && $respPos !== false && $respPos > $payloadPos) {
        $payloadJson = substr($rest, $payloadPos + 8, $respPos - ($payloadPos + 8));
        $payload = json_decode($payloadJson, true);

        if (is_array($payload)) {
            if (!empty($payload['caller_id'])) {
                $row['caller_id'] = $payload['caller_id'];
            }

            if (empty($row['zip_code']) && !empty($payload['zip_code'])) {
                $row['zip_code'] = $payload['zip_code'];
            }

            if (!empty($payload['service_category'])) {
                $row['service_category'] = $payload['service_category'];
            }

            if (!empty($payload['zip_source'])) {
                $row['zip_source'] = prefer_zip_source($row['zip_source'], (string)$payload['zip_source']);
            }

            if (!empty($payload['twilio_zip'])) {
                $row['twilio_zip'] = prefer_value($row['twilio_zip'], (string)$payload['twilio_zip']);
            }
        }
    }

    if ($respPos !== false) {
        $respJson = substr($rest, $respPos + 6);
        $resp = json_decode($respJson, true);

        if (is_array($resp)) {
            if (isset($resp['http_code'])) {
                $row['endpoint_http'] = $resp['http_code'];
            }

            if (isset($resp['json']['data']['bid'])) {
                $row['bid'] = $resp['json']['data']['bid'];
            }

            if (isset($resp['json']['data']['buyer_id'])) {
                $row['buyer_id'] = $resp['json']['data']['buyer_id'];
            }

            if (isset($resp['json']['data']['request_id']) && empty($row['request_id'])) {
                $row['request_id'] = $resp['json']['data']['request_id'];
            }

            if (isset($resp['json']['data']['phone_number'])) {
                $row['dial_to'] = $resp['json']['data']['phone_number'];
            }
        }
    }

    return $row;
}

function parse_twilio_line(string $line): ?array {
    $line = trim($line);
    if ($line === '') return null;

    if (!preg_match('/^\[(.*?)\]\s+([A-Z_]+):?\s*(.*)$/', $line, $m)) {
        return null;
    }

    $ts    = trim($m[1]);
    $event = trim($m[2]);
    $rest  = trim($m[3]);

    $row = [
        'ts' => $ts,
        'event' => $event,
        'callSid' => '',
        'from' => '',
        'to' => '',
        'vertical' => '',
        'step' => '',
        'digits' => '',
        'zip_source' => '',
        'twilio_zip' => '',
        'cached_zip' => '',
        'retry_zip' => '',
        'original_zip' => '',
        'raw' => $line,
    ];

    if (preg_match('/callSid=([A-Za-z0-9]+)/', $rest, $x)) {
        $row['callSid'] = $x[1];
    }
    if (preg_match('/from=([+\d]+)/', $rest, $x)) {
        $row['from'] = $x[1];
    }
    if (preg_match('/to=([+\d]+)/', $rest, $x)) {
        $row['to'] = $x[1];
    }
    if (preg_match('/vertical=([A-Za-z0-9_]+)/', $rest, $x)) {
        $row['vertical'] = $x[1];
    }
    if (preg_match('/step=([A-Za-z0-9_]+)/', $rest, $x)) {
        $row['step'] = $x[1];
    }
    if (preg_match('/digits=([0-9]+)/', $rest, $x)) {
        $row['digits'] = $x[1];
    }

    $row['zip_source'] = parse_key_value_from_rest($rest, 'zip_source');
    $row['twilio_zip'] = parse_key_value_from_rest($rest, 'twilio_zip');
    $row['cached_zip'] = parse_key_value_from_rest($rest, 'cached_zip');

    if (preg_match('/retry_zip=([0-9]{5})/', $rest, $x)) {
        $row['retry_zip'] = $x[1];
    }

    if (preg_match('/original_zip=([0-9]{5})/', $rest, $x)) {
        $row['original_zip'] = $x[1];
    }

    return $row;
}

function row_matches_filters(array $row, array $filters): bool {
    $dateFrom = trim((string)($filters['date_from'] ?? ''));
    $dateTo   = trim((string)($filters['date_to'] ?? ''));
    $caller   = trim((string)($filters['caller'] ?? ''));
    $zip      = trim((string)($filters['zip'] ?? ''));
    $vertical = trim((string)($filters['vertical'] ?? ''));
    $callsid  = trim((string)($filters['callsid'] ?? ''));

    $rowDate = '';
    if (!empty($row['ts'])) {
        $rowDate = substr((string)$row['ts'], 0, 10);
    }

    if ($dateFrom !== '' && $rowDate !== '' && $rowDate < $dateFrom) return false;
    if ($dateTo !== '' && $rowDate !== '' && $rowDate > $dateTo) return false;

    if ($callsid !== '' && !str_contains_ci((string)($row['callSid'] ?? ''), $callsid)) return false;

    if ($caller !== '') {
        $hay = (string)($row['caller_id'] ?? $row['from'] ?? '');
        if (!str_contains_ci($hay, $caller)) return false;
    }

    if ($zip !== '') {
        $hay = (string)($row['zip_code'] ?? $row['digits'] ?? $row['twilio_zip'] ?? '');
        if (!str_contains_ci($hay, $zip)) return false;
    }

    if ($vertical !== '') {
        $hay = (string)($row['service_category'] ?? $row['vertical'] ?? $row['sd_vertical'] ?? $row['tw_vertical'] ?? '');
        if (!str_contains_ci($hay, $vertical)) return false;
    }

    return true;
}

function read_log_rows(string $type, ?string $path, array $filters, int $page, int $perPage, int &$total = 0): array {
    $rows = [];

    if (!$path || !is_file($path)) {
        $total = 0;
        return [];
    }

    $fh = fopen($path, 'r');
    if (!$fh) {
        $total = 0;
        return [];
    }

    while (($line = fgets($fh)) !== false) {
        $row = null;

        if ($type === 'sd') {
            $row = parse_sd_line($line);
        } elseif ($type === 'twilio') {
            $row = parse_twilio_line($line);
        }

        if (!$row) continue;
        if (!row_matches_filters($row, $filters)) continue;

        $rows[] = $row;
    }
    fclose($fh);

    $rows = array_reverse($rows);
    $total = count($rows);

    $offset = max(0, ($page - 1) * $perPage);
    return array_slice($rows, $offset, $perPage);
}

function blank_merged_row(string $sid): array {
    return [
        'callSid' => $sid,
        'tw_ts' => '',
        'from' => '',
        'tw_vertical' => '',
        'sd_ts' => '',
        'zip' => '',
        'sd_vertical' => '',
        'bid' => '',
        'buyer_id' => '',
        'request_id' => '',
        'dial_to' => '',
        'zip_source' => '',
        'twilio_zip' => '',
        'sd_zip_source' => '',
        'sd_twilio_zip' => '',
        'tw_zip_source' => '',
        'tw_twilio_zip' => '',
        'retry_zip' => '',
        'original_zip' => '',
        'tw_events' => [],
        'sd_events' => [],
    ];
}

function read_merged_rows(array $paths, array $filters, int $page, int $perPage, int &$total = 0): array {
    $bucket = [];

    $twilioPath = $paths['twilio'] ?? null;
    if ($twilioPath && is_file($twilioPath)) {
        $fh = fopen($twilioPath, 'r');
        if ($fh) {
            while (($line = fgets($fh)) !== false) {
                $t = parse_twilio_line($line);
                if (!$t) continue;

                $twFilterRow = [
                    'ts' => $t['ts'] ?? '',
                    'callSid' => $t['callSid'] ?? '',
                    'from' => $t['from'] ?? '',
                    'vertical' => $t['vertical'] ?? '',
                ];

                if (!row_matches_filters($twFilterRow, [
                    'date_from' => $filters['date_from'] ?? '',
                    'date_to'   => $filters['date_to'] ?? '',
                    'caller'    => $filters['caller'] ?? '',
                    'callsid'   => $filters['callsid'] ?? '',
                    'vertical'  => '',
                    'zip'       => '',
                ])) {
                    continue;
                }

                $sid = $t['callSid'] ?? '';
                if ($sid === '') continue;

                if (!isset($bucket[$sid])) {
                    $bucket[$sid] = blank_merged_row($sid);
                }

                if ($bucket[$sid]['tw_ts'] === '') $bucket[$sid]['tw_ts'] = $t['ts'] ?? '';
                if ($bucket[$sid]['from'] === '') $bucket[$sid]['from'] = $t['from'] ?? '';
                if ($bucket[$sid]['tw_vertical'] === '') $bucket[$sid]['tw_vertical'] = $t['vertical'] ?? '';

                if (is_meaningful_zip_source($t['zip_source'] ?? '')) {
                    $bucket[$sid]['tw_zip_source'] = prefer_zip_source($bucket[$sid]['tw_zip_source'], $t['zip_source']);
                    $bucket[$sid]['zip_source'] = prefer_zip_source($bucket[$sid]['zip_source'], $t['zip_source']);
                }

                if (!empty($t['twilio_zip'])) {
                    $bucket[$sid]['tw_twilio_zip'] = prefer_value($bucket[$sid]['tw_twilio_zip'], $t['twilio_zip']);
                    $bucket[$sid]['twilio_zip'] = prefer_value($bucket[$sid]['twilio_zip'], $t['twilio_zip']);
                }

                if (!empty($t['retry_zip'])) {
                    $bucket[$sid]['retry_zip'] = prefer_value($bucket[$sid]['retry_zip'], $t['retry_zip']);
                }

                if (!empty($t['original_zip'])) {
                    $bucket[$sid]['original_zip'] = prefer_value($bucket[$sid]['original_zip'], $t['original_zip']);
                }

                $bucket[$sid]['tw_events'][] = $t['event'] ?? '';
            }
            fclose($fh);
        }
    }

    $sdPath = $paths['sd'] ?? null;
    if ($sdPath && is_file($sdPath)) {
        $fh = fopen($sdPath, 'r');
        if ($fh) {
            while (($line = fgets($fh)) !== false) {
                $s = parse_sd_line($line);
                if (!$s) continue;

                $sdFilterRow = [
                    'ts' => $s['ts'] ?? '',
                    'callSid' => $s['callSid'] ?? '',
                    'caller_id' => $s['caller_id'] ?? '',
                    'zip_code' => $s['zip_code'] ?? '',
                    'service_category' => $s['service_category'] ?? '',
                    'twilio_zip' => $s['twilio_zip'] ?? '',
                ];

                if (!row_matches_filters($sdFilterRow, $filters)) {
                    continue;
                }

                $sid = $s['callSid'] ?? '';
                if ($sid === '') continue;

                if (!isset($bucket[$sid])) {
                    $bucket[$sid] = blank_merged_row($sid);
                }

                if ($bucket[$sid]['sd_ts'] === '') $bucket[$sid]['sd_ts'] = $s['ts'] ?? '';
                if ($bucket[$sid]['zip'] === '') $bucket[$sid]['zip'] = $s['zip_code'] ?? '';
                if ($bucket[$sid]['sd_vertical'] === '') $bucket[$sid]['sd_vertical'] = $s['service_category'] ?? '';
                if ($bucket[$sid]['bid'] === '') $bucket[$sid]['bid'] = $s['bid'] ?? '';
                if ($bucket[$sid]['buyer_id'] === '') $bucket[$sid]['buyer_id'] = $s['buyer_id'] ?? '';
                if ($bucket[$sid]['request_id'] === '') $bucket[$sid]['request_id'] = $s['request_id'] ?? '';
                if ($bucket[$sid]['dial_to'] === '') $bucket[$sid]['dial_to'] = $s['dial_to'] ?? '';

                if (is_meaningful_zip_source($s['zip_source'] ?? '')) {
                    $bucket[$sid]['sd_zip_source'] = prefer_zip_source($bucket[$sid]['sd_zip_source'], $s['zip_source']);

                    // SD PING is the final buyer-routing ZIP source, so it should override weak/earlier TW values.
                    $bucket[$sid]['zip_source'] = prefer_zip_source($bucket[$sid]['zip_source'], $s['zip_source']);
                }

                if (!empty($s['twilio_zip'])) {
                    $bucket[$sid]['sd_twilio_zip'] = prefer_value($bucket[$sid]['sd_twilio_zip'], $s['twilio_zip']);
                    $bucket[$sid]['twilio_zip'] = prefer_value($bucket[$sid]['twilio_zip'], $s['twilio_zip']);
                }

                if (!empty($s['retry_zip'])) {
                    $bucket[$sid]['retry_zip'] = prefer_value($bucket[$sid]['retry_zip'], $s['retry_zip']);
                }

                if (!empty($s['original_zip'])) {
                    $bucket[$sid]['original_zip'] = prefer_value($bucket[$sid]['original_zip'], $s['original_zip']);
                }

                $bucket[$sid]['sd_events'][] = $s['event'] ?? '';
            }
            fclose($fh);
        }
    }

    $rows = [];
    foreach ($bucket as $sid => $r) {
        $verticalHay = trim((string)($r['sd_vertical'] ?: $r['tw_vertical']));

        if (($filters['vertical'] ?? '') !== '' && !str_contains_ci($verticalHay, (string)$filters['vertical'])) {
            continue;
        }

        // Final normalization before display.
        if (is_meaningful_zip_source($r['sd_zip_source'] ?? '')) {
            $r['zip_source'] = $r['sd_zip_source'];
        } elseif (is_meaningful_zip_source($r['tw_zip_source'] ?? '')) {
            $r['zip_source'] = $r['tw_zip_source'];
        } else {
            $r['zip_source'] = '';
        }

        if (!empty($r['sd_twilio_zip'])) {
            $r['twilio_zip'] = $r['sd_twilio_zip'];
        } elseif (!empty($r['tw_twilio_zip'])) {
            $r['twilio_zip'] = $r['tw_twilio_zip'];
        }

        $r['tw_events'] = implode(', ', array_filter($r['tw_events']));
        $r['sd_events'] = implode(', ', array_filter($r['sd_events']));

        $rows[] = $r;
    }

    usort($rows, function ($a, $b) {
        $ta = (string)($a['sd_ts'] ?: $a['tw_ts']);
        $tb = (string)($b['sd_ts'] ?: $b['tw_ts']);
        return strcmp($tb, $ta);
    });

    $total = count($rows);
    $offset = max(0, ($page - 1) * $perPage);

    return array_slice($rows, $offset, $perPage);
}