from itertools import count

from db import get_connection


FORMATION_PRESETS = {
    "4-4-2": {
        "gk": [{"x": 50, "y": 10}],
        "def": [
            {"x": 18, "y": 28},
            {"x": 39, "y": 24},
            {"x": 61, "y": 24},
            {"x": 82, "y": 28},
        ],
        "mid": [
            {"x": 18, "y": 48},
            {"x": 39, "y": 44},
            {"x": 61, "y": 44},
            {"x": 82, "y": 48},
        ],
        "fwd": [
            {"x": 38, "y": 70},
            {"x": 62, "y": 70},
        ],
    },
    "4-3-3": {
        "gk": [{"x": 50, "y": 10}],
        "def": [
            {"x": 18, "y": 28},
            {"x": 39, "y": 24},
            {"x": 61, "y": 24},
            {"x": 82, "y": 28},
        ],
        "mid": [
            {"x": 30, "y": 47},
            {"x": 50, "y": 42},
            {"x": 70, "y": 47},
        ],
        "fwd": [
            {"x": 20, "y": 72},
            {"x": 50, "y": 66},
            {"x": 80, "y": 72},
        ],
    },
    "4-2-3-1": {
        "gk": [{"x": 0, "y": 0}],
        "def": [
            {"x": 18, "y": 28},
            {"x": 39, "y": 24},
            {"x": 61, "y": 24},
            {"x": 82, "y": 28},
        ],
        "mid": [
            {"x": 35, "y": 42},  # опорник
            {"x": 65, "y": 42},  # опорник
            {"x": 20, "y": 58},  # левый
            {"x": 50, "y": 52},  # центр
            {"x": 80, "y": 58},  # правый
        ],
        "fwd": [
            {"x": 50, "y": 72},
        ],
    },
    "3-5-2": {
        "gk": [{"x": 50, "y": 10}],
        "def": [
            {"x": 30, "y": 26},
            {"x": 50, "y": 22},
            {"x": 70, "y": 26},
        ],
        "mid": [
            {"x": 10, "y": 50},  # левый фланг
            {"x": 35, "y": 46},
            {"x": 50, "y": 42},
            {"x": 65, "y": 46},
            {"x": 90, "y": 50},  # правый фланг
        ],
        "fwd": [
            {"x": 38, "y": 72},
            {"x": 62, "y": 72},
        ],
    },
    "5-3-2": {
        "gk": [{"x": 50, "y": 10}],
        "def": [
            {"x": 10, "y": 30},  # левый латераль
            {"x": 30, "y": 26},
            {"x": 50, "y": 22},
            {"x": 70, "y": 26},
            {"x": 90, "y": 30},  # правый латераль
        ],
        "mid": [
            {"x": 30, "y": 48},
            {"x": 50, "y": 44},
            {"x": 70, "y": 48},
        ],
        "fwd": [
            {"x": 38, "y": 72},
            {"x": 62, "y": 72},
        ],
    },
}


def detect_player_line(position: str) -> str:
    pos = (position or "").strip().lower()

    if not pos:
        return "mid"

    if "вр" in pos or "gk" in pos or "goalkeeper" in pos:
        return "gk"

    defender_markers = ["цз", "лз", "пз", "з", "def", "cb", "lb", "rb", "wb"]
    midfielder_markers = [
        "цп",
        "цоп",
        "оп",
        "п",
        "пзщ",
        "mid",
        "cm",
        "dm",
        "am",
        "lm",
        "rm",
    ]
    forward_markers = ["н", "цф", "ф", "lf", "rf", "fw", "st", "cf", "нап"]

    if any(marker in pos for marker in defender_markers):
        return "def"

    if any(marker in pos for marker in midfielder_markers):
        return "mid"

    if any(marker in pos for marker in forward_markers):
        return "fwd"

    return "mid"


def get_match_formations(match_id: int, team_id: int) -> list[dict]:
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                SELECT
                    mf.player_id,
                    mf.player_name,
                    mf.number,
                    p.position,
                    mf.is_captain,
                    mf.x,
                    mf.y,
                    p.last_name
                FROM match_formations mf
                LEFT JOIN players p ON p.id = mf.player_id
                WHERE mf.match_id = %s
                  AND mf.team_id = %s
                ORDER BY mf.id
                """,
                (match_id, team_id),
            )
            rows = cur.fetchall()

        return [
            {
                "player_id": row[0],
                "player_name": row[1] or "",
                "number": row[2] or "",
                "position": row[3] or "",
                "is_captain": bool(row[4]),
                "x": float(row[5]),
                "y": float(row[6]),
                "last_name": row[7] or "",
            }
            for row in rows
        ]
    finally:
        conn.close()


def replace_match_formations(match_id: int, team_id: int, players: list[dict]) -> None:
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                DELETE FROM match_formations
                WHERE match_id = %s AND team_id = %s
                """,
                (match_id, team_id),
            )
            for player in players:
                cur.execute(
                    """
                    INSERT INTO match_formations (
                        match_id,
                        team_id,
                        player_id,
                        player_name,
                        number,
                        position,
                        is_captain,
                        x,
                        y,
                        created_at,
                        updated_at
                    )
                    VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
                    """,
                    (
                        match_id,
                        team_id,
                        player.get("player_id"),
                        (player.get("player_name") or "").strip(),
                        (player.get("number") or "").strip(),
                        (player.get("position") or "").strip(),
                        bool(player.get("is_captain")),
                        float(player.get("x", 50)),
                        float(player.get("y", 50)),
                    ),
                )
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()


def parse_position(position: str) -> tuple[str, str]:
    """
    Возвращает:
    line: gk / def / mid / fwd
    side: left / center / right
    """
    pos = (position or "").strip().lower()

    if not pos:
        return "mid", "center"

    # Вратарь
    if "вр" in pos or "gk" in pos or "goalkeeper" in pos:
        return "gk", "center"

    # Сторона
    if pos.startswith("л"):
        side = "left"
    elif pos.startswith("п"):
        side = "right"
    else:
        side = "center"

    # Линия
    # Защита: ЛЗ, ПЗ, ЦЗ, ЛЦЗ, ПЦЗ и т.п.
    if "з" in pos:
        return "def", side

    # Нападение: Н, Ф, ЦФ, ЛФ, ПФ и т.п.
    if "ф" in pos or "н" in pos:
        return "fwd", side

    # Полузащита: П, ЦП, ЦОП, ЛП, ПП, ПЦП, ЛЦП и т.п.
    if "п" in pos:
        return "mid", side

    return "mid", side


def sort_players_by_side(players: list[dict]) -> list[dict]:
    side_order = {
        "left": 0,
        "center": 1,
        "right": 2,
    }

    return sorted(
        players,
        key=lambda p: (
            side_order.get(p.get("position", "")[1], 1),
            str(p.get("number", "")),
            str(p.get("last_name", "") or p.get("player_name", "")),
        ),
    )


def apply_formation_preset_to_players(
    players: list[dict], preset_name: str
) -> list[dict]:
    preset = FORMATION_PRESETS.get(preset_name)
    if not preset:
        raise ValueError(f"Unknown formation preset: {preset_name}")

    players = players[:11]

    gk, defs, mids, fwds = [], [], [], []

    for p in players:
        line, _ = parse_position(p.get("position"))

        if line == "gk":
            gk.append(p)
        elif line == "def":
            defs.append(p)
        elif line == "mid":
            mids.append(p)
        elif line == "fwd":
            fwds.append(p)
        else:
            mids.append(p)

    defs = sort_players_by_side(defs)
    mids = sort_players_by_side(mids)
    fwds = sort_players_by_side(fwds)

    leftovers = []

    def trim_or_collect(group: list[dict], count: int) -> list[dict]:
        if len(group) > count:
            leftovers.extend(group[count:])
            return group[:count]
        return group

    gk = trim_or_collect(gk, len(preset["gk"]))
    defs = trim_or_collect(defs, len(preset["def"]))
    mids = trim_or_collect(mids, len(preset["mid"]))
    fwds = trim_or_collect(fwds, len(preset["fwd"]))

    # если вратарь не найден — берём первого доступного
    if not gk:
        source = defs or mids or fwds or leftovers
        if source:
            gk = [source.pop(0)]

    def fill(group: list[dict], count: int) -> list[dict]:
        while len(group) < count and leftovers:
            group.append(leftovers.pop(0))
        return group

    gk = fill(gk, len(preset["gk"]))
    defs = fill(defs, len(preset["def"]))
    mids = fill(mids, len(preset["mid"]))
    fwds = fill(fwds, len(preset["fwd"]))

    result = []

    def assign(group: list[dict], coords: list[dict]):
        for p, c in zip(group, coords):
            result.append(
                {
                    "player_id": p.get("player_id"),
                    "player_name": p.get("player_name") or "",
                    "last_name": p.get("last_name") or "",
                    "number": p.get("number") or "",
                    "position": p.get("position") or "",
                    "is_captain": bool(p.get("is_captain")),
                    "x": c["x"],
                    "y": c["y"],
                }
            )

    assign(gk, preset["gk"])
    assign(defs, preset["def"])
    assign(mids, preset["mid"])
    assign(fwds, preset["fwd"])

    return result
