<?php
/**
 * Import results from chess-results.com Excel export (.xlsx).
 * Export the round pairings/results from chess-results (Excel-Export) and upload here.
 * Identifies the result column by header (Result, Risultato, Ergebnis, etc.) and board column (Sc., Bo., Bd.).
 *
 * @package Chess_Podium
 */

if (!defined('ABSPATH')) {
    exit;
}

final class ChessPodium_ChessResultsExcelImport
{
    private const RESULT_HEADERS = ['result', 'res.', 'res', 'risultato', 'ergebnis'];
    private const BOARD_HEADERS = ['bo.', 'bo', 'bd.', 'bd', 'board', 'sc.', 'sc', 'sc.nr.'];
    private const BLACK_HEADERS = ['black', 'schwarz', 'nero'];

    /**
     * Parse Excel file and extract results by board.
     *
     * @param string $filePath Full path to .xlsx file (chess-results Excel export)
     * @return array{success: bool, results?: array<int, array{result: string, bye_points?: float}>, error?: string}
     */
    public static function parse(string $filePath): array
    {
        $rows = ChessPodium_XlsxReader::read($filePath);
        if ($rows === false) {
            return [
                'success' => false,
                'error' => 'import_cr_error',
            ];
        }
        if (empty($rows)) {
            return ['success' => false, 'error' => 'import_cr_empty_file'];
        }

        $colMap = self::find_columns($rows);
        if ($colMap === null) {
            if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
                $firstRow = $rows[0] ?? [];
                error_log('[Chess Podium] Excel import: no Result column. Row0 headers: ' . implode(' | ', array_map('strval', $firstRow)));
            }
            return ['success' => false, 'error' => 'import_cr_no_results_column'];
        }

        $results = [];
        $headerRowIdx = $colMap['header_row'];
        $dataRows = array_slice($rows, $headerRowIdx + 1);

        foreach ($dataRows as $row) {
            $board = (int) trim((string) ($row[$colMap['board_col']] ?? ''));
            if ($board <= 0) {
                continue;
            }

            $resultCell = trim((string) ($row[$colMap['result_col']] ?? ''));
            $blackCell = isset($colMap['black_col']) ? trim((string) ($row[$colMap['black_col']] ?? '')) : '';

            $isBye = stripos($blackCell, 'bye') !== false || $blackCell === '-';

            $normalized = self::normalize_result($resultCell, $isBye);
            if ($normalized === null) {
                continue;
            }

            $entry = ['result' => $normalized['result']];
            if (isset($normalized['bye_points'])) {
                $entry['bye_points'] = $normalized['bye_points'];
            }
            $results[$board] = $entry;
        }

        if (empty($results)) {
            return ['success' => false, 'error' => 'import_cr_error'];
        }

        ksort($results, SORT_NUMERIC);
        return ['success' => true, 'results' => $results];
    }

    /**
     * Find header row and column indices for board, result, black.
     *
     * @param array<array<string|int|float>> $rows
     * @return array{header_row: int, board_col: int, result_col: int, black_col?: int}|null
     */
    private static function find_columns(array $rows): ?array
    {
        for ($i = 0; $i < min(35, count($rows)); $i++) {
            $row = $rows[$i];
            $boardCol = null;
            $resultCol = null;
            $blackCol = null;

            foreach ($row as $col => $val) {
                $h = strtolower(trim((string) $val));
                if ($h === '') {
                    continue;
                }
                if ($boardCol === null && in_array($h, self::BOARD_HEADERS, true)) {
                    $boardCol = $col;
                }
                if ($resultCol === null && in_array($h, self::RESULT_HEADERS, true)) {
                    $resultCol = $col;
                }
                if ($blackCol === null && in_array($h, self::BLACK_HEADERS, true)) {
                    $blackCol = $col;
                }
            }

            if ($resultCol !== null) {
                return [
                    'header_row' => $i,
                    'board_col' => $boardCol ?? 0,
                    'result_col' => $resultCol,
                    'black_col' => $blackCol,
                ];
            }
        }
        return null;
    }

    /**
     * Normalize chess-results result string to Chess Podium format.
     *
     * @param string $raw
     * @param bool $isBye
     * @return array{result: string, bye_points?: float}|null
     */
    private static function normalize_result(string $raw, bool $isBye): ?array
    {
        $raw = preg_replace('/\s+/', ' ', trim($raw));
        if ($raw === '') {
            return null;
        }

        if ($isBye) {
            $pts = 0.5;
            if (preg_match('/^1$|^1\.0$/i', $raw)) {
                $pts = 1.0;
            } elseif (preg_match('/^0\.5$|^½$/u', $raw)) {
                $pts = 0.5;
            } elseif (preg_match('/^0$|^0\.0$/i', $raw)) {
                $pts = 0.0;
            }
            return ['result' => 'BYE', 'bye_points' => $pts];
        }

        $lower = strtolower($raw);
        if (preg_match('/^1\s*-\s*0$|^1:0$/i', $raw)) {
            return ['result' => '1-0'];
        }
        if (preg_match('/^0\s*-\s*1$|^0:1$/i', $raw)) {
            return ['result' => '0-1'];
        }
        if (preg_match('/^(½|1\/2|0\.5)\s*-\s*(½|1\/2|0\.5)$/iu', $raw) || $lower === '½-½' || $lower === '0.5-0.5') {
            return ['result' => '1/2-1/2'];
        }
        // chess-results: "- - +" = vittoria nero per forfait (bianco assente). Simbolo arbitrale.
        if (preg_match('/(\\\\?\s*)?-\s*(\\\\?\s*)?-\s*\+/', $raw)) {
            return ['result' => '0F'];
        }
        if ($raw === '+' || $lower === 'forfeit' || $lower === 'w.o.') {
            return ['result' => '1F'];
        }
        if ($raw === '-' || $lower === 'forfeit loss') {
            return ['result' => '0F'];
        }

        return null;
    }
}
