"""
Mini-projet 01 — Script CLI Gestionnaire de Tâches
Formation Python Backend

Usage:
    python solution.py add "titre" [--priorite low|medium|high|critical]
    python solution.py list [--statut todo|in_progress|done|cancelled]
    python solution.py update <id> --statut <statut>
    python solution.py delete <id>
"""

from __future__ import annotations
import argparse
import json
import sys
from dataclasses import dataclass, field, asdict
from datetime import datetime
from pathlib import Path
from typing import Optional


# ── Configuration ──────────────────────────────────────────
DATA_FILE = Path(__file__).parent / "tasks.json"
STATUTS_VALIDES   = ["todo", "in_progress", "done", "cancelled"]
PRIORITES_VALIDES = ["low", "medium", "high", "critical"]


# ── Modèle ─────────────────────────────────────────────────
@dataclass
class Task:
    id: int
    titre: str
    statut: str = "todo"
    priorite: str = "medium"
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def __str__(self) -> str:
        return f"[{self.priorite.upper():8}] #{self.id:3} {self.titre[:40]:<42} {self.statut}"


# ── Exception ──────────────────────────────────────────────
class TaskNotFoundError(Exception):
    def __init__(self, task_id: int):
        super().__init__(f"Tâche #{task_id} introuvable")
        self.task_id = task_id


# ── Persistance ─────────────────────────────────────────────
def load_tasks() -> list[Task]:
    if not DATA_FILE.exists():
        return []
    with DATA_FILE.open("r", encoding="utf-8") as f:
        data = json.load(f)
    return [Task(**d) for d in data]


def save_tasks(tasks: list[Task]) -> None:
    with DATA_FILE.open("w", encoding="utf-8") as f:
        json.dump([asdict(t) for t in tasks], f, ensure_ascii=False, indent=2)


def next_id(tasks: list[Task]) -> int:
    return max((t.id for t in tasks), default=0) + 1


# ── Commandes CRUD ──────────────────────────────────────────
def cmd_add(args: argparse.Namespace) -> None:
    tasks = load_tasks()
    t = Task(
        id=next_id(tasks),
        titre=args.titre,
        priorite=args.priorite or "medium"
    )
    tasks.append(t)
    save_tasks(tasks)
    print(f"✓ Tâche ajoutée : {t}")


def cmd_list(args: argparse.Namespace) -> None:
    tasks = load_tasks()
    if args.statut:
        tasks = [t for t in tasks if t.statut == args.statut]
    if not tasks:
        print("Aucune tâche trouvée.")
        return

    header = f"{'ID':>4}  {'TITRE':<42}  {'STATUT':<14}  {'PRIORITE'}"
    print(header)
    print("─" * len(header))
    for t in tasks:
        couleur = {"done": "\033[32m", "in_progress": "\033[33m", "cancelled": "\033[31m"}.get(t.statut, "")
        reset = "\033[0m" if couleur else ""
        print(f"{t.id:>4}  {t.titre[:42]:<42}  {couleur}{t.statut:<14}{reset}  {t.priorite}")


def cmd_update(args: argparse.Namespace) -> None:
    tasks = load_tasks()
    tache = next((t for t in tasks if t.id == args.id), None)
    if tache is None:
        raise TaskNotFoundError(args.id)
    if args.statut:
        tache.statut = args.statut
    if args.priorite:
        tache.priorite = args.priorite
    if args.titre:
        tache.titre = args.titre
    save_tasks(tasks)
    print(f"✓ Tâche mise à jour : {tache}")


def cmd_delete(args: argparse.Namespace) -> None:
    tasks = load_tasks()
    avant = len(tasks)
    tasks = [t for t in tasks if t.id != args.id]
    if len(tasks) == avant:
        raise TaskNotFoundError(args.id)
    save_tasks(tasks)
    print(f"✓ Tâche #{args.id} supprimée.")


# ── CLI ─────────────────────────────────────────────────────
def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Gestionnaire de tâches CLI — Formation Python Backend",
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    sub = parser.add_subparsers(dest="commande", required=True)

    # add
    p_add = sub.add_parser("add", help="Ajouter une tâche")
    p_add.add_argument("titre", help="Titre de la tâche")
    p_add.add_argument("--priorite", choices=PRIORITES_VALIDES, default="medium")

    # list
    p_list = sub.add_parser("list", help="Lister les tâches")
    p_list.add_argument("--statut", choices=STATUTS_VALIDES)

    # update
    p_upd = sub.add_parser("update", help="Mettre à jour une tâche")
    p_upd.add_argument("id", type=int, help="ID de la tâche")
    p_upd.add_argument("--statut",   choices=STATUTS_VALIDES)
    p_upd.add_argument("--priorite", choices=PRIORITES_VALIDES)
    p_upd.add_argument("--titre",    type=str)

    # delete
    p_del = sub.add_parser("delete", help="Supprimer une tâche")
    p_del.add_argument("id", type=int, help="ID de la tâche")

    return parser


def main() -> int:
    parser = build_parser()
    args   = parser.parse_args()

    COMMANDES = {
        "add":    cmd_add,
        "list":   cmd_list,
        "update": cmd_update,
        "delete": cmd_delete,
    }

    try:
        COMMANDES[args.commande](args)
        return 0
    except TaskNotFoundError as e:
        print(f"✗ Erreur : {e}", file=sys.stderr)
        return 1
    except KeyboardInterrupt:
        return 0


if __name__ == "__main__":
    sys.exit(main())
