from __future__ import annotations

import csv
import io
import itertools
import zipfile
from pathlib import Path
from typing import Iterable

import httpx

from .config import HTTP_TIMEOUT, USER_AGENT


def _delimiter_from_header(header: str) -> str:
    return ";" if header.count(";") >= header.count(",") else ","


def _normalise(value: str) -> str:
    return "".join(ch.lower() for ch in value if ch.isalnum())


def choose_code_column(headers: Iterable[str], candidates: Iterable[str]) -> str | None:
    normalised = {_normalise(h): h for h in headers}
    for candidate in candidates:
        key = _normalise(candidate)
        if key in normalised:
            return normalised[key]
    return None


def filter_csv_lines(lines: Iterable[str], output_path: Path, codes: set[str], candidate_columns: list[str]) -> dict[str, object]:
    iterator = iter(lines)
    try:
        header = next(iterator)
    except StopIteration:
        return {"rows_read": 0, "rows_written": 0, "code_column": None}
    delimiter = _delimiter_from_header(header)
    reader = csv.DictReader(itertools.chain([header], iterator), delimiter=delimiter)
    code_column = choose_code_column(reader.fieldnames or [], candidate_columns)
    if not code_column:
        return {"rows_read": 0, "rows_written": 0, "code_column": None, "headers": reader.fieldnames}

    output_path.parent.mkdir(parents=True, exist_ok=True)
    rows_read = 0
    rows_written = 0
    with output_path.open("w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=reader.fieldnames, delimiter=delimiter)
        writer.writeheader()
        for row in reader:
            rows_read += 1
            if str(row.get(code_column, "")).strip() in codes:
                writer.writerow(row)
                rows_written += 1
    return {"rows_read": rows_read, "rows_written": rows_written, "code_column": code_column, "output": str(output_path)}


def download_filter_csv(url: str, output_path: Path, codes: set[str], candidate_columns: list[str]) -> dict[str, object]:
    headers = {"User-Agent": USER_AGENT}
    with httpx.Client(timeout=HTTP_TIMEOUT, follow_redirects=True, headers=headers) as client:
        response = client.get(url)
        response.raise_for_status()
        content = response.content

    if url.lower().endswith(".zip") or response.headers.get("content-type", "").lower().startswith("application/zip"):
        with zipfile.ZipFile(io.BytesIO(content)) as zf:
            csv_names = [n for n in zf.namelist() if n.lower().endswith(".csv")]
            if not csv_names:
                return {"error": "Aucun CSV trouvé dans le ZIP", "url": url}
            with zf.open(csv_names[0]) as f:
                text = io.TextIOWrapper(f, encoding="utf-8", errors="replace")
                return filter_csv_lines(text, output_path, codes, candidate_columns)

    text = content.decode("utf-8", errors="replace")
    return filter_csv_lines(text.splitlines(), output_path, codes, candidate_columns)
