#!/usr/bin/env python3
"""
Script pour générer la documentation Markdown à partir des fichiers Gherkin (.feature)
Convertit les fichiers .feature en fichiers .md pour MkDocs Material
"""

import os
import re
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass, field


@dataclass
class Step:
    """Représente une étape Gherkin"""
    keyword: str  # Étant donné, Quand, Alors, Et, Mais
    text: str
    table: List[List[str]] = field(default_factory=list)


@dataclass
class Scenario:
    """Représente un scénario ou plan de scénario"""
    title: str
    is_outline: bool
    steps: List[Step] = field(default_factory=list)
    examples: List[List[str]] = field(default_factory=list)


@dataclass
class Feature:
    """Représente une fonctionnalité complète"""
    title: str
    description: str = ""
    context_steps: List[Step] = field(default_factory=list)
    scenarios: List[Scenario] = field(default_factory=list)


def parse_table(lines: List[str], start_idx: int) -> Tuple[List[List[str]], int]:
    """Parse une table Gherkin et retourne (table, nouvel index)"""
    table = []
    idx = start_idx
    while idx < len(lines) and lines[idx].strip().startswith('|'):
        row = [cell.strip() for cell in lines[idx].strip().strip('|').split('|')]
        table.append(row)
        idx += 1
    return table, idx


def parse_feature_content(content: str) -> Feature:
    """Parse le contenu d'un fichier .feature"""
    # Supprimer la ligne de langage
    content = re.sub(r'^# language: \w+\n', '', content)

    lines = content.split('\n')
    feature = Feature(title="")

    i = 0
    current_scenario: Optional[Scenario] = None
    in_context = False
    in_examples = False

    while i < len(lines):
        line = lines[i]
        stripped = line.strip()

        # Ligne vide
        if not stripped:
            i += 1
            continue

        # Fonctionnalité
        if stripped.startswith('Fonctionnalité:'):
            feature.title = stripped[len('Fonctionnalité:'):].strip()
            i += 1
            # Capturer la description (lignes indentées qui suivent)
            desc_lines = []
            while i < len(lines) and lines[i].startswith('  ') and not lines[i].strip().startswith(('Contexte:', 'Scénario:', 'Plan du Scénario:')):
                desc_lines.append(lines[i].strip())
                i += 1
            feature.description = '\n'.join(desc_lines)
            continue

        # Contexte
        if stripped == 'Contexte:':
            in_context = True
            in_examples = False
            i += 1
            continue

        # Plan du Scénario
        if stripped.startswith('Plan du Scénario:'):
            if current_scenario:
                feature.scenarios.append(current_scenario)
            current_scenario = Scenario(
                title=stripped[len('Plan du Scénario:'):].strip(),
                is_outline=True
            )
            in_context = False
            in_examples = False
            i += 1
            continue

        # Scénario
        if stripped.startswith('Scénario:'):
            if current_scenario:
                feature.scenarios.append(current_scenario)
            current_scenario = Scenario(
                title=stripped[len('Scénario:'):].strip(),
                is_outline=False
            )
            in_context = False
            in_examples = False
            i += 1
            continue

        # Exemples (pour Plan du Scénario)
        if stripped == 'Exemples:':
            in_examples = True
            i += 1
            continue

        # Table d'exemples
        if in_examples and stripped.startswith('|') and current_scenario:
            table, i = parse_table(lines, i)
            current_scenario.examples = table
            in_examples = False
            continue

        # Étapes Gherkin
        keywords = ['Étant donné', 'Quand', 'Alors', 'Et', 'Mais']
        matched_keyword = None
        for kw in keywords:
            if stripped.startswith(kw):
                matched_keyword = kw
                break

        if matched_keyword:
            step_text = stripped[len(matched_keyword):].strip()
            step = Step(keyword=matched_keyword, text=step_text)

            # Vérifier si une table suit
            if i + 1 < len(lines) and lines[i + 1].strip().startswith('|'):
                table, i = parse_table(lines, i + 1)
                step.table = table
            else:
                i += 1

            # Ajouter l'étape au bon endroit
            if in_context:
                feature.context_steps.append(step)
            elif current_scenario:
                current_scenario.steps.append(step)
            continue

        i += 1

    # Ajouter le dernier scénario
    if current_scenario:
        feature.scenarios.append(current_scenario)

    return feature


def format_table_markdown(table: List[List[str]], indent: str = "") -> str:
    """Formate une table en Markdown"""
    if not table:
        return ""

    lines = []
    # En-tête
    lines.append(f"{indent}| " + " | ".join(table[0]) + " |")
    # Séparateur
    lines.append(f"{indent}|" + "|".join(["---" for _ in table[0]]) + "|")
    # Lignes de données
    for row in table[1:]:
        lines.append(f"{indent}| " + " | ".join(row) + " |")

    return '\n'.join(lines)


def get_keyword_color(keyword: str) -> str:
    """Retourne une couleur pour chaque type de mot-clé"""
    colors = {
        'Étant donné': '#2196F3',  # Bleu
        'Quand': '#FF9800',         # Orange
        'Alors': '#4CAF50',         # Vert
        'Et': '#9E9E9E',            # Gris
        'Mais': '#F44336'           # Rouge
    }
    return colors.get(keyword, '#000000')


def format_step(step: Step, indent: str = "") -> str:
    """Formate une étape Gherkin avec couleur"""
    color = get_keyword_color(step.keyword)
    result = f'{indent}<span style="color: {color}">**{step.keyword}**</span> {step.text}'

    if step.table:
        result += "\n\n"
        result += format_table_markdown(step.table, indent + "    ")
        result += "\n"

    return result


def generate_markdown(feature: Feature) -> str:
    """Génère le contenu Markdown pour une fonctionnalité"""
    lines = []

    # Titre de la fonctionnalité
    lines.append(f"# {feature.title}\n")

    # Description en tant que user story (bloc quote formaté proprement)
    if feature.description:
        desc_lines = feature.description.split('\n')
        for desc_line in desc_lines:
            lines.append(f"> *{desc_line}*\n")
        lines.append("\n")

    # Statistiques
    scenario_count = len(feature.scenarios)
    outline_count = sum(1 for s in feature.scenarios if s.is_outline)
    regular_count = scenario_count - outline_count

    stats_parts = [f"**{scenario_count} scénario{'s' if scenario_count > 1 else ''}**"]
    if outline_count > 0:
        stats_parts.append(f" ({regular_count} standard{'s' if regular_count > 1 else ''}, {outline_count} plan{'s' if outline_count > 1 else ''})")
    lines.append("".join(stats_parts) + "\n\n")

    lines.append("---\n\n")

    # Contexte
    if feature.context_steps:
        lines.append('!!! info "Contexte commun à tous les scénarios"\n\n')
        for step in feature.context_steps:
            lines.append(format_step(step, "    ") + "\n")
        lines.append("\n")

    # Scénarios
    for idx, scenario in enumerate(feature.scenarios, 1):
        # Header h2 pour chaque scénario
        if scenario.is_outline:
            lines.append(f"## {idx}. 📋 Plan: {scenario.title}\n\n")
        else:
            lines.append(f"## {idx}. {scenario.title}\n\n")

        # Regrouper les étapes par section (Given/When/Then)
        current_section = None
        for step in scenario.steps:
            # Déterminer la section
            if step.keyword == 'Étant donné':
                section = 'given'
            elif step.keyword == 'Quand':
                section = 'when'
            elif step.keyword == 'Alors':
                section = 'then'
            else:
                section = current_section  # Et/Mais gardent la section précédente

            # Ajouter un séparateur visuel entre les sections
            if section != current_section and current_section is not None:
                lines.append("\n")
            current_section = section

            lines.append(format_step(step, "") + "\n")

        # Exemples pour les plans de scénario
        if scenario.is_outline and scenario.examples:
            lines.append("\n**📊 Exemples de données:**\n\n")
            lines.append(format_table_markdown(scenario.examples, "") + "\n")

        lines.append("\n---\n\n")

    return ''.join(lines)


def generate_index(features_dir: Path, output_dir: Path) -> None:
    """Génère un fichier index.md listant toutes les fonctionnalités"""
    categories: Dict[str, List[Tuple[str, Path, int, int]]] = {}

    for feature_file in features_dir.rglob('*.feature'):
        category = feature_file.parent.name
        relative_path = feature_file.relative_to(features_dir)
        md_path = relative_path.with_suffix('.md')

        # Extraire le titre et compter les scénarios
        with open(feature_file, 'r', encoding='utf-8') as f:
            content = f.read()
            feature_match = re.search(r'^Fonctionnalité: (.+)$', content, re.MULTILINE)
            title = feature_match.group(1) if feature_match else feature_file.stem

            scenario_count = len(re.findall(r'^\s{2}Scénario:', content, re.MULTILINE))
            outline_count = len(re.findall(r'^\s{2}Plan du Scénario:', content, re.MULTILINE))

        if category not in categories:
            categories[category] = []
        categories[category].append((title, md_path, scenario_count, outline_count))

    # Calculer les totaux
    total_features = sum(len(cat) for cat in categories.values())
    total_scenarios = sum(s + o for cat in categories.values() for _, _, s, o in cat)

    # Générer l'index
    index_lines = [
        "# Tests BDD - Documentation des fonctionnalités\n\n",
        "Cette documentation est générée automatiquement à partir des fichiers Gherkin (`.feature`).\n\n",
        "## Vue d'ensemble\n\n",
        f"| Métrique | Valeur |\n",
        f"|----------|--------|\n",
        f"| Fonctionnalités | **{total_features}** |\n",
        f"| Scénarios | **{total_scenarios}** |\n",
        f"| Domaines métier | **{len(categories)}** |\n\n",
        "---\n\n"
    ]

    # Icônes par catégorie
    category_icons = {
        'authentication': '🔐',
        'recommendation': '🎯',
        'interest-gauges': '📊',
        'content-creation': '🎨',
        'navigation': '🧭',
        'publicites': '📢',
        'radio-live': '📻',
        'abonnements': '🔔',
        'monetisation': '💰',
        'premium': '⭐',
        'mode-offline': '📴',
        'error-handling': '⚠️',
        'rgpd-compliance': '🔒',
        'moderation': '🛡️',
        'partage': '🔗',
        'profil': '👤',
        'recherche': '🔍',
        'audio-guides': '🎧',
    }

    for category in sorted(categories.keys()):
        category_title = category.replace('-', ' ').replace('_', ' ').title()
        icon = category_icons.get(category, '📁')
        cat_scenarios = sum(s + o for _, _, s, o in categories[category])

        index_lines.append(f"## {icon} {category_title}\n\n")

        # Carte de la catégorie
        index_lines.append(f"| Fonctionnalité | Scénarios |\n")
        index_lines.append(f"|----------------|:---------:|\n")

        for title, md_path, scenario_count, outline_count in sorted(categories[category]):
            link = str(md_path).replace('\\', '/')
            total = scenario_count + outline_count
            index_lines.append(f"| [{title}]({link}) | {total} |\n")

        index_lines.append(f"\n*{len(categories[category])} fonctionnalités • {cat_scenarios} scénarios*\n\n")

    # Écrire l'index
    index_file = output_dir / 'index.md'
    with open(index_file, 'w', encoding='utf-8') as f:
        f.writelines(index_lines)

    print(f"✓ Index généré: {index_file}")


def main():
    """Point d'entrée principal"""
    # Chemins
    project_root = Path(__file__).parent.parent
    features_dir = project_root / 'features'
    output_dir = project_root / 'docs' / 'bdd'

    # Nettoyer le dossier de sortie
    if output_dir.exists():
        import shutil
        shutil.rmtree(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    print(f"📁 Génération de la documentation BDD...")
    print(f"   Source: {features_dir}")
    print(f"   Destination: {output_dir}\n")

    # Convertir tous les fichiers .feature
    feature_files = list(features_dir.rglob('*.feature'))
    count = 0

    for feature_file in feature_files:
        # Créer la structure de dossiers
        relative_path = feature_file.relative_to(features_dir)
        md_file = output_dir / relative_path.with_suffix('.md')
        md_file.parent.mkdir(parents=True, exist_ok=True)

        # Parser et convertir
        with open(feature_file, 'r', encoding='utf-8') as f:
            content = f.read()

        feature = parse_feature_content(content)
        markdown = generate_markdown(feature)

        # Écrire le fichier
        with open(md_file, 'w', encoding='utf-8') as f:
            f.write(markdown)

        count += 1
        print(f"  ✓ {relative_path} → {md_file.relative_to(project_root)}")

    print(f"\n✓ {count} fichiers .feature convertis en Markdown")

    # Générer l'index
    print("\n📋 Génération de l'index...")
    generate_index(features_dir, output_dir)

    print(f"\n✅ Documentation BDD générée avec succès dans {output_dir}")


if __name__ == '__main__':
    main()
