#!/usr/bin/env python3
"""
Script pour générer un tableau de bord web pour le suivi d'un livre.
Ce script crée une page HTML dans le dossier 'build' avec des visualisations interactives:
- Graphique de réseau des personnages (déplaçable à la souris)
- Suivi de rédaction
- Graphique des intrigues
"""

import os
import csv
import json
import shutil
from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import make_interp_spline

# Assurez-vous que le dossier build existe
BUILD_DIR = 'build'
if not os.path.exists(BUILD_DIR):
    os.makedirs(BUILD_DIR)

# Créer le dossier pour les ressources statiques s'il n'existe pas
STATIC_DIR = os.path.join(BUILD_DIR, 'static')
if not os.path.exists(STATIC_DIR):
    os.makedirs(STATIC_DIR)

# Créer les sous-dossiers pour CSS, JS et images
CSS_DIR = os.path.join(STATIC_DIR, 'css')
JS_DIR = os.path.join(STATIC_DIR, 'js')
IMG_DIR = os.path.join(STATIC_DIR, 'img')

for directory in [CSS_DIR, JS_DIR, IMG_DIR]:
    if not os.path.exists(directory):
        os.makedirs(directory)

def load_csv_data(file_path, delimiter=','):
    """Charge les données d'un fichier CSV."""
    if not os.path.exists(file_path):
        print(f"Erreur: Le fichier {file_path} n'existe pas.")
        return []
    
    with open(file_path, 'r', encoding='utf-8') as f:
        reader = csv.reader(f, delimiter=delimiter)
        return list(reader)

def process_character_occurrences(csv_file='occurrences_personnages.csv'):
    """
    Traite le fichier d'occurrences des personnages pour générer les données
    du graphique de réseau.
    """
    data = load_csv_data(csv_file)
    if not data:
        return None
    
    # Extraire les en-têtes (noms des personnages)
    headers = data[0]
    characters = headers[1:]  # Ignorer la première colonne (Chapitre)
    
    # Créer les nœuds pour chaque personnage
    nodes = [{"id": char, "name": char, "group": 1} for char in characters]
    
    # Créer les liens entre personnages qui apparaissent dans les mêmes chapitres
    links = []
    for row in data[1:]:  # Ignorer la ligne d'en-tête
        if len(row) < len(headers):
            continue
            
        chapter = row[0]
        # Trouver les personnages présents dans ce chapitre
        present_chars = []
        for i, count in enumerate(row[1:], 1):
            if count and int(count) > 0:
                present_chars.append(headers[i])
        
        # Créer des liens entre tous les personnages présents
        for i in range(len(present_chars)):
            for j in range(i+1, len(present_chars)):
                # Vérifier si le lien existe déjà
                link_exists = False
                for link in links:
                    if (link["source"] == present_chars[i] and link["target"] == present_chars[j]) or \
                       (link["source"] == present_chars[j] and link["target"] == present_chars[i]):
                        link["value"] += 1  # Incrémenter la force du lien
                        link_exists = True
                        break
                
                # Si le lien n'existe pas, le créer
                if not link_exists:
                    links.append({
                        "source": present_chars[i],
                        "target": present_chars[j],
                        "value": 1
                    })
    
    return {"nodes": nodes, "links": links}

def process_character_plot_network(char_csv='occurrences_personnages.csv', plot_csv='intrigues.csv'):
    """
    Traite les fichiers d'occurrences des personnages et d'intrigues pour générer
    les données du graphique de réseau reliant personnages et intrigues.
    """
    # Charger les données des personnages
    char_data = load_csv_data(char_csv)
    if not char_data:
        return None
    
    # Charger les données des intrigues
    plot_data = load_csv_data(plot_csv)
    if not plot_data or len(plot_data) < 2:
        return None
    
    # Extraire les en-têtes (noms des personnages)
    char_headers = char_data[0]
    characters = char_headers[1:]  # Ignorer la première colonne (Chapitre)
    
    # Créer un dictionnaire pour stocker les chapitres où chaque personnage apparaît
    character_chapters = {char: [] for char in characters}
    
    # Remplir le dictionnaire avec les chapitres où chaque personnage apparaît
    for row in char_data[1:]:  # Ignorer la ligne d'en-tête
        if len(row) < len(char_headers):
            continue
            
        chapter = row[0]
        # Extraire le numéro de chapitre si possible
        chapter_num = None
        try:
            # Essayer d'extraire un nombre du nom du chapitre
            import re
            match = re.search(r'(\d+)', chapter)
            if match:
                chapter_num = int(match.group(1))
        except:
            pass
        
        # Si on n'a pas pu extraire un nombre, continuer
        if chapter_num is None:
            continue
        
        # Trouver les personnages présents dans ce chapitre
        for i, count in enumerate(row[1:], 1):
            if count and int(count) > 0:
                character_chapters[char_headers[i]].append(chapter_num)
    
    # Créer les nœuds pour chaque personnage (groupe 1)
    nodes = [{"id": char, "name": char, "group": 1, "type": "character"} for char in characters]
    
    # Créer les nœuds pour chaque intrigue (groupe 2)
    for row in plot_data[1:]:  # Ignorer la ligne d'en-tête
        if len(row) < 3:
            continue
        
        try:
            start = int(row[0])
            end = int(row[1])
            name = row[2]
            
            nodes.append({
                "id": f"plot_{name}",
                "name": name,
                "group": 2,
                "type": "plot",
                "start": start,
                "end": end
            })
        except (ValueError, IndexError):
            continue
    
    # Créer les liens entre personnages et intrigues
    links = []
    
    # Pour chaque intrigue
    for node in nodes:
        if node["group"] == 2:  # C'est une intrigue
            plot_start = node["start"]
            plot_end = node["end"]
            
            # Pour chaque personnage
            for char_node in nodes:
                if char_node["group"] == 1:  # C'est un personnage
                    char_name = char_node["id"]
                    
                    # Vérifier si le personnage apparaît dans un chapitre couvert par l'intrigue
                    appears_in_plot = False
                    for chapter in character_chapters[char_name]:
                        if plot_start <= chapter <= plot_end:
                            appears_in_plot = True
                            break
                    
                    # Si le personnage apparaît dans l'intrigue, créer un lien
                    if appears_in_plot:
                        links.append({
                            "source": char_name,
                            "target": node["id"],
                            "value": 1
                        })
    
    return {"nodes": nodes, "links": links}

def process_writing_progress(csv_file='suivi_livre.csv'):
    """
    Traite le fichier de suivi pour générer les données du graphique de progression.
    """
    try:
        # Lire le fichier CSV avec pandas pour faciliter le traitement des dates
        df = pd.read_csv(csv_file, delimiter=';', comment='#',
                         names=['date', 'mots', 'intrigues', 'personnages', 'personnages_mots', 'chapitres', 'sous_chapitres'])
        
        # Convertir les dates
        df['date'] = pd.to_datetime(df['date'])
        df['date_str'] = df['date'].dt.strftime('%Y-%m-%d')
        
        # Grouper par jour et prendre la dernière valeur de chaque jour
        df = df.sort_values('date')
        df = df.drop_duplicates('date_str', keep='last')
        
        # Préparer les données pour le graphique
        dates = df['date_str'].tolist()
        words = df['mots'].tolist()
        chapters = df['chapitres'].tolist()
        
        return {
            'dates': dates,
            'words': words,
            'chapters': chapters
        }
    except Exception as e:
        print(f"Erreur lors du traitement du fichier de suivi: {e}")
        return None

def process_plot_timeline(csv_file='intrigues.csv'):
    """
    Traite le fichier d'intrigues pour générer les données du graphique de Gantt.
    """
    data = load_csv_data(csv_file)
    if not data or len(data) < 2:
        return None
    
    # Extraire les en-têtes
    headers = data[0]
    if len(headers) < 3:
        print("Format de fichier d'intrigues invalide.")
        return None
    
    # Créer la structure de données pour le graphique de Gantt
    plots = []
    for row in data[1:]:
        if len(row) < 3:
            continue
        
        try:
            start = int(row[0])
            end = int(row[1])
            name = row[2]
            
            plots.append({
                'name': name,
                'start': start,
                'end': end
            })
        except (ValueError, IndexError) as e:
            print(f"Erreur lors du traitement d'une ligne d'intrigue: {e}")
            continue
    
    return plots

def create_css():
    """Crée le fichier CSS pour le tableau de bord."""
    css_content = """
    body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        margin: 0;
        padding: 0;
        background-color: #f5f5f5;
        color: #333;
    }
    
    .container {
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
    }
    
    header {
        background-color: #2c3e50;
        color: white;
        padding: 20px;
        text-align: center;
        margin-bottom: 30px;
    }
    
    h1, h2, h3 {
        margin-top: 0;
    }
    
    .dashboard-section {
        background-color: white;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        padding: 20px;
        margin-bottom: 30px;
    }
    
    .chart-container {
        width: 100%;
        height: 400px;
        margin: 20px 0;
    }
    
    #network-graph, #character-plot-network, #plot-timeline {
        border: 1px solid #ddd;
        border-radius: 4px;
    }
    
    .stats-container {
        display: flex;
        justify-content: space-between;
        flex-wrap: wrap;
        margin-bottom: 20px;
    }
    
    .stat-card {
        background-color: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        padding: 15px;
        width: calc(25% - 20px);
        margin-bottom: 20px;
        text-align: center;
    }
    
    .stat-card h3 {
        margin-bottom: 5px;
        font-size: 16px;
        color: #666;
    }
    
    .stat-card .value {
        font-size: 24px;
        font-weight: bold;
        color: #2c3e50;
    }
    
    /* Styles pour le graphique de réseau personnages-intrigues */
    .character text {
        font-weight: bold;
        fill: #333;
    }
    
    .plot text {
        fill: #e74c3c;
    }
    
    .legend {
        font-size: 12px;
    }
    
    @media (max-width: 768px) {
        .stat-card {
            width: calc(50% - 20px);
        }
    }
    
    @media (max-width: 480px) {
        .stat-card {
            width: 100%;
        }
    }
    
    footer {
        text-align: center;
        padding: 20px;
        color: #666;
        font-size: 14px;
    }
    """
    
    with open(os.path.join(CSS_DIR, 'dashboard.css'), 'w', encoding='utf-8') as f:
        f.write(css_content)

def create_network_graph_js(network_data):
    """Crée le fichier JavaScript pour le graphique de réseau."""
    js_content = f"""
    // Données du graphique de réseau
    const networkData = {json.dumps(network_data)};
    
    // Fonction pour initialiser le graphique de réseau
    function initNetworkGraph() {{
        const width = document.getElementById('network-graph').clientWidth;
        const height = 400;
        
        // Créer le SVG
        const svg = d3.select("#network-graph")
            .append("svg")
            .attr("width", width)
            .attr("height", height);
        
        // Créer la simulation de force
        const simulation = d3.forceSimulation(networkData.nodes)
            .force("link", d3.forceLink(networkData.links).id(d => d.id).distance(100))
            .force("charge", d3.forceManyBody().strength(-200))
            .force("center", d3.forceCenter(width / 2, height / 2))
            .force("collision", d3.forceCollide().radius(30));
        
        // Créer les liens
        const link = svg.append("g")
            .attr("class", "links")
            .selectAll("line")
            .data(networkData.links)
            .enter().append("line")
            .attr("stroke-width", d => Math.sqrt(d.value))
            .attr("stroke", "#999")
            .attr("stroke-opacity", 0.6);
        
        // Créer les nœuds
        const node = svg.append("g")
            .attr("class", "nodes")
            .selectAll("g")
            .data(networkData.nodes)
            .enter().append("g")
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));
        
        // Ajouter des cercles pour les nœuds
        node.append("circle")
            .attr("r", 10)
            .attr("fill", "#69b3a2");
        
        // Ajouter des étiquettes pour les nœuds
        node.append("text")
            .text(d => d.name)
            .attr("x", 12)
            .attr("y", 3)
            .style("font-size", "12px");
        
        // Mettre à jour la position des éléments à chaque tick de la simulation
        simulation.on("tick", () => {{
            link
                .attr("x1", d => d.source.x)
                .attr("y1", d => d.source.y)
                .attr("x2", d => d.target.x)
                .attr("y2", d => d.target.y);
            
            node
                .attr("transform", d => `translate(${{d.x}},${{d.y}})`);
        }});
        
        // Fonctions pour le drag & drop
        function dragstarted(event, d) {{
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }}
        
        function dragged(event, d) {{
            d.fx = event.x;
            d.fy = event.y;
        }}
        
        function dragended(event, d) {{
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }}
    }}
    
    // Initialiser le graphique quand la page est chargée
    document.addEventListener('DOMContentLoaded', initNetworkGraph);
    """
    
    with open(os.path.join(JS_DIR, 'network-graph.js'), 'w', encoding='utf-8') as f:
        f.write(js_content)

def create_character_plot_network_js(network_data):
    """Crée le fichier JavaScript pour le graphique de réseau personnages-intrigues."""
    js_content = f"""
    // Données du graphique de réseau personnages-intrigues
    const characterPlotData = {json.dumps(network_data)};
    
    // Fonction pour initialiser le graphique de réseau personnages-intrigues
    function initCharacterPlotNetwork() {{
        const width = document.getElementById('character-plot-network').clientWidth;
        const height = 400;
        
        // Créer le SVG
        const svg = d3.select("#character-plot-network")
            .append("svg")
            .attr("width", width)
            .attr("height", height);
        
        // Définir les couleurs pour les différents types de nœuds
        const nodeColors = {{
            character: "#69b3a2",  // Vert pour les personnages
            plot: "#e74c3c"        // Rouge pour les intrigues
        }};
        
        // Créer la simulation de force
        const simulation = d3.forceSimulation(characterPlotData.nodes)
            .force("link", d3.forceLink(characterPlotData.links).id(d => d.id).distance(150))
            .force("charge", d3.forceManyBody().strength(-300))
            .force("center", d3.forceCenter(width / 2, height / 2))
            .force("collision", d3.forceCollide().radius(30));
        
        // Créer les liens
        const link = svg.append("g")
            .attr("class", "links")
            .selectAll("line")
            .data(characterPlotData.links)
            .enter().append("line")
            .attr("stroke-width", d => Math.sqrt(d.value))
            .attr("stroke", "#999")
            .attr("stroke-opacity", 0.6);
        
        // Créer les nœuds
        const node = svg.append("g")
            .attr("class", "nodes")
            .selectAll("g")
            .data(characterPlotData.nodes)
            .enter().append("g")
            .attr("class", d => d.type)
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));
        
        // Ajouter des cercles pour les nœuds
        node.append("circle")
            .attr("r", d => d.type === "character" ? 8 : 12)
            .attr("fill", d => nodeColors[d.type]);
        
        // Ajouter des étiquettes pour les nœuds
        // Les étiquettes des personnages sont toujours visibles
        node.filter(d => d.type === "character")
            .append("text")
            .text(d => d.name)
            .attr("x", 12)
            .attr("y", 3)
            .style("font-size", "12px")
            .style("font-weight", "bold");
        
        // Les étiquettes des intrigues ne sont visibles que lors du survol
        const plotLabels = node.filter(d => d.type === "plot")
            .append("text")
            .text(d => d.name)
            .attr("x", 12)
            .attr("y", 3)
            .style("font-size", "12px")
            .style("font-weight", "bold")
            .style("fill", "#e74c3c")
            .style("opacity", 0);  // Initialement invisible
        
        // Ajouter des événements de survol pour les nœuds d'intrigue
        node.filter(d => d.type === "plot")
            .on("mouseover", function(event, d) {{
                // Rendre l'étiquette visible
                d3.select(this).select("text").style("opacity", 1);
                // Mettre en évidence les liens connectés
                link.style("stroke", l => 
                    l.source.id === d.id || l.target.id === d.id ? "#e74c3c" : "#999")
                    .style("stroke-opacity", l => 
                    l.source.id === d.id || l.target.id === d.id ? 1 : 0.2)
                    .style("stroke-width", l => 
                    l.source.id === d.id || l.target.id === d.id ? Math.sqrt(l.value) * 2 : Math.sqrt(l.value));
                // Mettre en évidence les personnages connectés
                node.style("opacity", n => 
                    n.id === d.id || characterPlotData.links.some(l => 
                        (l.source.id === d.id && l.target.id === n.id) || 
                        (l.target.id === d.id && l.source.id === n.id)) ? 1 : 0.4);
            }})
            .on("mouseout", function() {{
                // Cacher l'étiquette
                d3.select(this).select("text").style("opacity", 0);
                // Restaurer les liens
                link.style("stroke", "#999")
                    .style("stroke-opacity", 0.6)
                    .style("stroke-width", d => Math.sqrt(d.value));
                // Restaurer les nœuds
                node.style("opacity", 1);
            }});
        
        // Mettre à jour la position des éléments à chaque tick de la simulation
        simulation.on("tick", () => {{
            link
                .attr("x1", d => d.source.x)
                .attr("y1", d => d.source.y)
                .attr("x2", d => d.target.x)
                .attr("y2", d => d.target.y);
            
            node
                .attr("transform", d => `translate(${{d.x}},${{d.y}})`);
        }});
        
        // Fonctions pour le drag & drop
        function dragstarted(event, d) {{
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }}
        
        function dragged(event, d) {{
            d.fx = event.x;
            d.fy = event.y;
        }}
        
        function dragended(event, d) {{
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }}
        
        // Ajouter une légende
        const legend = svg.append("g")
            .attr("class", "legend")
            .attr("transform", "translate(20, 20)");
        
        // Légende pour les personnages
        legend.append("circle")
            .attr("r", 6)
            .attr("fill", nodeColors.character);
        
        legend.append("text")
            .attr("x", 15)
            .attr("y", 4)
            .text("Personnages")
            .style("font-size", "12px");
        
        // Légende pour les intrigues
        legend.append("circle")
            .attr("r", 6)
            .attr("fill", nodeColors.plot)
            .attr("transform", "translate(0, 20)");
        
        legend.append("text")
            .attr("x", 15)
            .attr("y", 24)
            .text("Intrigues (survoler pour voir le nom)")
            .style("font-size", "12px");
    }}
    
    // Initialiser le graphique quand la page est chargée
    document.addEventListener('DOMContentLoaded', initCharacterPlotNetwork);
    """
    
    with open(os.path.join(JS_DIR, 'character-plot-network.js'), 'w', encoding='utf-8') as f:
        f.write(js_content)

def create_progress_chart_js(progress_data):
    """Crée le fichier JavaScript pour le graphique de progression."""
    js_content = f"""
    // Données du graphique de progression
    const progressData = {json.dumps(progress_data)};
    
    // Fonction pour initialiser le graphique de progression
    function initProgressChart() {{
        const ctx = document.getElementById('progress-chart').getContext('2d');
        
        const chart = new Chart(ctx, {{
            type: 'line',
            data: {{
                labels: progressData.dates,
                datasets: [
                    {{
                        label: 'Mots',
                        data: progressData.words,
                        borderColor: '#3498db',
                        backgroundColor: 'rgba(52, 152, 219, 0.1)',
                        borderWidth: 2,
                        fill: true,
                        tension: 0.4
                    }},
                    {{
                        label: 'Chapitres',
                        data: progressData.chapters,
                        borderColor: '#e74c3c',
                        backgroundColor: 'rgba(231, 76, 60, 0.1)',
                        borderWidth: 2,
                        fill: true,
                        tension: 0.4,
                        yAxisID: 'y1'
                    }}
                ]
            }},
            options: {{
                responsive: true,
                maintainAspectRatio: false,
                scales: {{
                    x: {{
                        title: {{
                            display: true,
                            text: 'Date'
                        }}
                    }},
                    y: {{
                        title: {{
                            display: true,
                            text: 'Nombre de mots'
                        }},
                        beginAtZero: true
                    }},
                    y1: {{
                        position: 'right',
                        title: {{
                            display: true,
                            text: 'Nombre de chapitres'
                        }},
                        beginAtZero: true,
                        grid: {{
                            drawOnChartArea: false
                        }}
                    }}
                }},
                plugins: {{
                    tooltip: {{
                        mode: 'index',
                        intersect: false
                    }},
                    legend: {{
                        position: 'top'
                    }}
                }}
            }}
        }});
    }}
    
    // Initialiser le graphique quand la page est chargée
    document.addEventListener('DOMContentLoaded', initProgressChart);
    """
    
    with open(os.path.join(JS_DIR, 'progress-chart.js'), 'w', encoding='utf-8') as f:
        f.write(js_content)

def create_plot_timeline_js(plot_data):
    """Crée le fichier JavaScript pour le graphique des intrigues."""
    js_content = f"""
    // Données du graphique des intrigues
    const plotData = {json.dumps(plot_data)};
    
    // Fonction pour initialiser le graphique des intrigues
    function initPlotTimeline() {{
        const width = document.getElementById('plot-timeline').clientWidth;
        const height = 400;
        const margin = {{ top: 50, right: 50, bottom: 50, left: 150 }};
        
        // Créer le SVG
        const svg = d3.select("#plot-timeline")
            .append("svg")
            .attr("width", width)
            .attr("height", height);
        
        // Trouver les valeurs min et max pour l'échelle
        const minStart = d3.min(plotData, d => d.start);
        const maxEnd = d3.max(plotData, d => d.end);
        
        // Créer les échelles
        const xScale = d3.scaleLinear()
            .domain([minStart, maxEnd])
            .range([margin.left, width - margin.right]);
        
        const yScale = d3.scaleBand()
            .domain(plotData.map(d => d.name))
            .range([margin.top, height - margin.bottom])
            .padding(0.2);
        
        // Créer les axes
        const xAxis = d3.axisBottom(xScale);
        const yAxis = d3.axisLeft(yScale);
        
        // Ajouter les axes au SVG
        svg.append("g")
            .attr("transform", `translate(0,${{height - margin.bottom}})`)
            .call(xAxis);
        
        svg.append("g")
            .attr("transform", `translate(${{margin.left}},0)`)
            .call(yAxis);
        
        // Ajouter les barres pour chaque intrigue
        svg.selectAll(".plot-bar")
            .data(plotData)
            .enter()
            .append("rect")
            .attr("class", "plot-bar")
            .attr("x", d => xScale(d.start))
            .attr("y", d => yScale(d.name))
            .attr("width", d => xScale(d.end) - xScale(d.start))
            .attr("height", yScale.bandwidth())
            .attr("fill", "#3498db")
            .attr("opacity", 0.7)
            .on("mouseover", function(event, d) {{
                d3.select(this).attr("opacity", 1);
                
                // Afficher une infobulle
                svg.append("text")
                    .attr("class", "tooltip")
                    .attr("x", xScale(d.start) + (xScale(d.end) - xScale(d.start)) / 2)
                    .attr("y", yScale(d.name) - 5)
                    .attr("text-anchor", "middle")
                    .text(`${{d.name}}: ${{d.start}} - ${{d.end}}`)
                    .style("font-size", "12px")
                    .style("font-weight", "bold");
            }})
            .on("mouseout", function() {{
                d3.select(this).attr("opacity", 0.7);
                svg.selectAll(".tooltip").remove();
            }});
        
        // Ajouter un titre
        svg.append("text")
            .attr("x", width / 2)
            .attr("y", margin.top / 2)
            .attr("text-anchor", "middle")
            .style("font-size", "16px")
            .style("font-weight", "bold")
            .text("Chronologie des intrigues");
    }}
    
    // Initialiser le graphique quand la page est chargée
    document.addEventListener('DOMContentLoaded', initPlotTimeline);
    """
    
    with open(os.path.join(JS_DIR, 'plot-timeline.js'), 'w', encoding='utf-8') as f:
        f.write(js_content)

def create_dashboard_html(network_data, progress_data, plot_data, character_plot_data=None):
    """Crée le fichier HTML pour le tableau de bord."""
    # Calculer quelques statistiques pour afficher
    total_words = progress_data['words'][-1] if progress_data['words'] else 0
    total_chapters = progress_data['chapters'][-1] if progress_data['chapters'] else 0
    total_characters = len(network_data['nodes']) if network_data else 0
    total_plots = len(plot_data) if plot_data else 0
    
    html_content = f"""<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tableau de Bord du Livre</title>
    <link rel="stylesheet" href="static/css/dashboard.css">
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <header>
        <h1>Tableau de Bord du Livre</h1>
        <p>Dernière mise à jour: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
    </header>
    
    <div class="container">
        <div class="dashboard-section">
            <h2>Statistiques Générales</h2>
            <div class="stats-container">
                <div class="stat-card">
                    <h3>Mots</h3>
                    <div class="value">{total_words}</div>
                </div>
                <div class="stat-card">
                    <h3>Chapitres</h3>
                    <div class="value">{total_chapters}</div>
                </div>
                <div class="stat-card">
                    <h3>Personnages</h3>
                    <div class="value">{total_characters}</div>
                </div>
                <div class="stat-card">
                    <h3>Intrigues</h3>
                    <div class="value">{total_plots}</div>
                </div>
            </div>
        </div>
        
        <div class="dashboard-section">
            <h2>Progression de l'Écriture</h2>
            <div class="chart-container">
                <canvas id="progress-chart"></canvas>
            </div>
        </div>
        
        <div class="dashboard-section">
            <h2>Réseau des Personnages</h2>
            <p>Cliquez et faites glisser les nœuds pour réorganiser le graphique.</p>
            <div class="chart-container" id="network-graph"></div>
        </div>
        
        <div class="dashboard-section">
            <h2>Réseau Personnages-Intrigues</h2>
            <p>Cliquez et faites glisser les nœuds pour réorganiser le graphique. Survolez les nœuds d'intrigue pour voir leur nom.</p>
            <div class="chart-container" id="character-plot-network"></div>
        </div>
        
        <div class="dashboard-section">
            <h2>Chronologie des Intrigues</h2>
            <div class="chart-container" id="plot-timeline"></div>
        </div>
    </div>
    
    <footer>
        <p>Généré automatiquement par generate_dashboard.py</p>
    </footer>
    
    <script src="static/js/network-graph.js"></script>
    <script src="static/js/character-plot-network.js"></script>
    <script src="static/js/progress-chart.js"></script>
    <script src="static/js/plot-timeline.js"></script>
</body>
</html>
"""
    
    with open(os.path.join(BUILD_DIR, 'dashboard.html'), 'w', encoding='utf-8') as f:
        f.write(html_content)

def main():
    """Fonction principale pour générer le tableau de bord."""
    print("Génération du tableau de bord...")
    
    # Traiter les données
    network_data = process_character_occurrences()
    progress_data = process_writing_progress()
    plot_data = process_plot_timeline()
    character_plot_data = process_character_plot_network()
    
    # Vérifier si les données sont disponibles
    if not network_data:
        network_data = {"nodes": [], "links": []}
        print("Avertissement: Données de réseau des personnages non disponibles.")
    
    if not progress_data:
        progress_data = {"dates": [], "words": [], "chapters": []}
        print("Avertissement: Données de progression non disponibles.")
    
    if not plot_data:
        plot_data = []
        print("Avertissement: Données d'intrigues non disponibles.")
        
    if not character_plot_data:
        character_plot_data = {"nodes": [], "links": []}
        print("Avertissement: Données de réseau personnages-intrigues non disponibles.")
    
    # Créer les fichiers CSS et JS
    create_css()
    create_network_graph_js(network_data)
    create_character_plot_network_js(character_plot_data)
    create_progress_chart_js(progress_data)
    create_plot_timeline_js(plot_data)
    
    # Créer le fichier HTML principal
    create_dashboard_html(network_data, progress_data, plot_data, character_plot_data)
    
    print(f"Tableau de bord généré avec succès dans {os.path.join(BUILD_DIR, 'dashboard.html')}")
    print("Ouvrez ce fichier dans votre navigateur pour voir le tableau de bord.")

if __name__ == "__main__":
    main()