// SPDX-License-Identifier: PMPL-1.0-or-later
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
// Codegen module for anvomidaviser — Orchestrates ISU element parsing, rule
// validation, and score calculation. Generates formal Anvomidav program
// descriptions from validated competition programs.

pub mod parser;
pub mod scorer;
pub mod validator;

use anyhow::{Context, Result};
use std::fs;

use crate::abi::{ProgramScore, TechnicalScore};
use crate::manifest::Manifest;

/// Generate all Anvomidav artifacts from a validated manifest.
///
/// This is the main codegen pipeline:
/// 1. Parse all ISU element codes from the manifest
/// 2. Validate against ISU technical rules for the given segment
/// 3. Calculate base values and produce a technical score summary
/// 4. Write the formal program description to the output directory
///
/// # Errors
/// Returns an error if any element cannot be parsed, rules are violated,
/// or output files cannot be written.
pub fn generate_all(manifest: &Manifest, output_dir: &str) -> Result<()> {
    fs::create_dir_all(output_dir).context("Failed to create output directory")?;

    // Step 1: Parse all element codes
    let codes: Vec<String> = manifest.elements.iter().map(|e| e.code.clone()).collect();
    let elements = parser::parse_program(&codes)?;

    // Step 2: Validate against ISU rules
    let violations = validator::validate_program(&elements, manifest.program.segment);

    // Step 3: Calculate scores
    let scored_elements: Vec<TechnicalScore> = elements
        .iter()
        .enumerate()
        .map(|(i, elem)| {
            let base_value = scorer::base_value(elem);
            // Elements in the second half of the program get 1.1x bonus.
            // For a free skate with 7 jump elements, the last 3 are in the second half.
            // For simplicity, we mark the last half of elements as second-half.
            let total_elements = elements.len();
            let second_half_start = total_elements / 2;
            let second_half_bonus = i >= second_half_start && elem.is_jump_element();
            TechnicalScore {
                element: elem.clone(),
                base_value,
                goe_adjustment: 0.0, // GOE requires judge input, default to 0
                second_half_bonus,
            }
        })
        .collect();

    let total_tes = ProgramScore::calculate_tes(&scored_elements);

    let program_score = ProgramScore {
        elements: scored_elements,
        violations: violations.clone(),
        total_tes,
    };

    // Step 4: Write formal program description
    let output_path = format!("{}/program_score.toml", output_dir);
    let output_content = generate_program_report(manifest, &program_score);
    fs::write(&output_path, &output_content)
        .with_context(|| format!("Failed to write {}", output_path))?;

    // Report results
    println!("=== anvomidaviser: {} ===", manifest.project.name);
    println!(
        "Discipline: {} | Segment: {} | Level: {}",
        manifest.program.discipline, manifest.program.segment, manifest.program.level
    );
    println!("Elements parsed: {}", program_score.elements.len());
    println!("Total TES (base): {:.2}", program_score.total_tes);

    if violations.is_empty() {
        println!("Validation: PASS — no rule violations");
    } else {
        println!("Validation: {} violation(s):", violations.len());
        for v in &violations {
            println!("  - {}", v);
        }
    }

    println!("Output: {}", output_path);
    Ok(())
}

/// Generate a human-readable and machine-parseable program score report.
fn generate_program_report(manifest: &Manifest, score: &ProgramScore) -> String {
    let mut report = String::new();

    report.push_str("# SPDX-License-Identifier: PMPL-1.0-or-later\n");
    report.push_str("# Generated by anvomidaviser — formal ISU program description\n\n");

    report.push_str("[program]\n");
    report.push_str(&format!("name = \"{}\"\n", manifest.project.name));
    report.push_str(&format!("discipline = \"{}\"\n", manifest.program.discipline));
    report.push_str(&format!("segment = \"{}\"\n", manifest.program.segment));
    report.push_str(&format!("level = \"{}\"\n", manifest.program.level));
    report.push_str(&format!("scoring-season = \"{}\"\n\n", manifest.scoring.goe_table));

    for (i, elem) in score.elements.iter().enumerate() {
        report.push_str(&format!("[[element]]\n"));
        report.push_str(&format!("index = {}\n", i + 1));
        report.push_str(&format!("code = \"{}\"\n", elem.element.notation()));
        report.push_str(&format!("base-value = {:.2}\n", elem.base_value));
        report.push_str(&format!("goe-adjustment = {:.2}\n", elem.goe_adjustment));
        report.push_str(&format!("second-half-bonus = {}\n", elem.second_half_bonus));
        report.push_str(&format!("total = {:.2}\n\n", elem.total()));
    }

    report.push_str("[summary]\n");
    report.push_str(&format!("total-tes = {:.2}\n", score.total_tes));
    report.push_str(&format!("violation-count = {}\n", score.violations.len()));

    if !score.violations.is_empty() {
        report.push_str("\n");
        for (i, v) in score.violations.iter().enumerate() {
            report.push_str(&format!("[[violation]]\n"));
            report.push_str(&format!("index = {}\n", i + 1));
            report.push_str(&format!("description = \"{}\"\n\n", v));
        }
    }

    report
}

/// Build the generated artifacts (placeholder for future Anvomidav compilation).
pub fn build(manifest: &Manifest, _release: bool) -> Result<()> {
    println!(
        "Building anvomidaviser program: {} ({} {})",
        manifest.project.name, manifest.program.segment, manifest.program.discipline
    );
    Ok(())
}

/// Run the generated program (placeholder for future Anvomidav execution).
pub fn run(manifest: &Manifest, _args: &[String]) -> Result<()> {
    println!(
        "Running anvomidaviser program: {} ({} {})",
        manifest.project.name, manifest.program.segment, manifest.program.discipline
    );
    Ok(())
}
