"""Chess board loader with auto-detection of source format."""

from __future__ import annotations

import io
import os
import re
import urllib.request
from typing import Any

import chess
import chess.pgn


def load_from_source(source: str) -> str:
    """Load a chess position from any source (auto-detect format).

    Supports:
    - FEN strings
    - PGN strings
    - URLs (Lichess, Chess.com, etc.)
    - Lichess game IDs (e.g., q7ZvsdUF)
    - File paths
    - stdin (use "-")

    Args:
        source: Source to load from

    Returns:
        FEN string of the position

    Raises:
        ValueError: If source cannot be parsed or loaded
    """
    # stdin
    if source == "-":
        import sys

        content = sys.stdin.read().strip()
        return _parse_content(content)

    # URL
    if source.startswith("http://") or source.startswith("https://"):
        return _load_from_url(source)

    # File path
    if os.path.exists(source):
        with open(source) as f:
            content = f.read().strip()
        return _parse_content(content)

    # Lichess game ID pattern (8 chars, alphanumeric)
    if re.match(r"^[a-zA-Z0-9]{8}$", source):
        return _load_from_lichess_game(source)

    # Try to parse as FEN or PGN directly
    return _parse_content(source)


def _parse_content(content: str) -> str:
    """Parse content as FEN or PGN.

    Args:
        content: String content to parse

    Returns:
        FEN string

    Raises:
        ValueError: If content cannot be parsed
    """
    # Try FEN first (most common)
    if _looks_like_fen(content):
        try:
            board = chess.Board(fen=content)
            return board.fen()
        except ValueError:
            pass

    # Try PGN
    try:
        game = chess.pgn.read_game(io.StringIO(content))
        if game is None:
            raise ValueError("Could not parse as PGN")

        # Get final position
        board = game.board()
        for move in game.mainline_moves():
            board.push(move)

        return board.fen()
    except Exception:
        pass

    raise ValueError(f"Could not parse content as FEN or PGN: {content[:100]}")


def _looks_like_fen(s: str) -> bool:
    """Check if string looks like FEN notation."""
    # FEN has rank descriptions separated by slashes
    # Basic heuristic: contains slashes and looks like board description
    parts = s.split()
    if not parts:
        return False

    board_part = parts[0]
    return "/" in board_part and any(c in board_part for c in "pnbrqkPNBRQK12345678")


def _load_from_url(url: str) -> str:
    """Load chess position from URL.

    Supports:
    - Lichess game URLs
    - Direct FEN/PGN URLs

    Args:
        url: URL to load from

    Returns:
        FEN string

    Raises:
        ValueError: If URL cannot be loaded or parsed
    """
    # Lichess game URL: https://lichess.org/q7ZvsdUF
    lichess_match = re.match(r"https?://lichess\.org/([a-zA-Z0-9]{8})", url)
    if lichess_match:
        game_id = lichess_match.group(1)
        return _load_from_lichess_game(game_id)

    # Generic URL - fetch and parse
    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            content = response.read().decode("utf-8")
        return _parse_content(content)
    except Exception as e:
        raise ValueError(f"Failed to load from URL {url}: {e}")


def _load_from_lichess_game(game_id: str) -> str:
    """Load position from Lichess game ID.

    Args:
        game_id: Lichess game ID (8 characters)

    Returns:
        FEN string of final position

    Raises:
        ValueError: If game cannot be loaded
    """
    url = f"https://lichess.org/game/export/{game_id}?evals=false&clocks=false"

    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            pgn_content = response.read().decode("utf-8")

        game = chess.pgn.read_game(io.StringIO(pgn_content))
        if game is None:
            raise ValueError(f"Could not parse Lichess game {game_id}")

        # Replay to final position
        board = game.board()
        for move in game.mainline_moves():
            board.push(move)

        return board.fen()

    except urllib.error.HTTPError as e:
        if e.code == 404:
            raise ValueError(f"Lichess game not found: {game_id}")
        raise ValueError(f"Failed to load Lichess game {game_id}: {e}")
    except Exception as e:
        raise ValueError(f"Failed to load Lichess game {game_id}: {e}")


def load_lichess_puzzle() -> tuple[str, dict[str, Any]]:
    """Load today's Lichess puzzle.

    Returns:
        Tuple of (FEN string, puzzle metadata)

    Raises:
        ValueError: If puzzle cannot be loaded
    """
    url = "https://lichess.org/api/puzzle/daily"

    try:
        import json

        with urllib.request.urlopen(url, timeout=10) as response:
            data = json.loads(response.read())

        pgn_str = data["game"]["pgn"]
        initial_ply = data["puzzle"]["initialPly"]

        # Parse PGN and replay to puzzle position
        game = chess.pgn.read_game(io.StringIO(f'[Event "?"]\n\n{pgn_str}'))
        if game is None:
            raise ValueError("Could not parse puzzle PGN")

        board = game.board()
        moves = list(game.mainline_moves())

        for i, move in enumerate(moves):
            if i >= initial_ply:
                break
            board.push(move)

        return board.fen(), data["puzzle"]

    except Exception as e:
        raise ValueError(f"Failed to load Lichess puzzle: {e}")
