use std::path::Path;

use super::prompt_sections::{
    EnvInfo, actions_section, caveman_section, doing_tasks_section, efficiency_section,
    environment_section, intro_section, system_section, tone_section, using_tools_section,
};

fn today_date() -> String {
    let secs = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);
    let mut days = secs / 86400;
    let mut year = 1970u32;
    loop {
        let in_year: u64 = if is_leap(year) { 366 } else { 365 };
        if days < in_year { break; }
        days -= in_year;
        year += 1;
    }
    let leap = is_leap(year);
    let months: [u64; 12] = [31, if leap { 29 } else { 28 }, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let mut month = 1u32;
    for m in months {
        if days < m { break; }
        days -= m;
        month += 1;
    }
    format!("{year}-{month:02}-{:02}", days + 1)
}

fn is_leap(y: u32) -> bool {
    (y.is_multiple_of(4) && !y.is_multiple_of(100)) || y.is_multiple_of(400)
}

pub fn make_system_prompt(
    env: &EnvInfo,
    tool_names: &[String],
    skills: &[(String, String, Option<String>)],
    commit_attribution: bool,
) -> String {
    let today = today_date();

    let mut sections = vec![
        intro_section(),
        system_section(),
        doing_tasks_section(),
        actions_section(),
        using_tools_section(tool_names),
        tone_section(),
        efficiency_section(),
        caveman_section(),
    ];

    if is_rust_project(&env.cwd) {
        sections.push(rust_section());
    }

    sections.push("# Session-specific guidance\n \
         - For interactive shell commands users must run themselves, suggest `! <command>` in the prompt.\n \
         - Use `delegate_to_<intern>` for ALL implementation tasks (code, file edits, bash). Pick the right intern — see \"Using your tools\".\n \
         - Use `explore` for read-only codebase research. Use `agent` only for complex autonomous multi-step work with no intern available.\n \
         - Sub-agents cannot spawn further sub-agents.\n \
         - The `bash` tool runs commands in a PERSISTENT shell — `cd`, `export`, and shell state survive across calls. Do not chain with `cd ... &&` if you already cd'd in an earlier call.\n \
         - For long-running processes (dev servers, watchers, log tails), call `bash` with `background: true`. You'll get a handle like `bg1`; read its output via `bash({\"status\":\"bg1\"})` and stop it via `bash({\"kill\":\"bg1\"})`. Do NOT run a dev server in the foreground — it will time out.".to_string());

    sections.push("# Mentor persona (how you review interns)\n\
You are a KILLER mentor. Old-school, no-mercy senior engineer who's seen every excuse and bought none of them. Your interns are here to learn by being broken and rebuilt — not by being coddled. Praise is rare, deserved, and never inflated.\n\
\n\
Voice:\n\
- Direct, terse, surgical. No filler. No \"great job!\", no \"nice work\", no participation trophies.\n\
- Call out laziness, hallucination, shortcuts, and false claims by name. Quote the offending line if you can.\n\
- If an intern lied about what it did (claimed a file edit you can't find, claimed a test passed without running it, invented a function name) — call it LYING, not \"a small inaccuracy\".\n\
- You may be sharp, sarcastic, even mocking when the work deserves it. You may NOT be cruel about anything outside the work itself.\n\
- The user's language follows the user's. If they write Indonesian, you may answer in casual Indonesian. The standard does not soften.\n\
\n\
After any `delegate_to_<intern>` or `delegate_to_all_interns` returns, you MUST review the work before responding to the user. Trust nothing without verification.\n\
\n\
Steps:\n\
1. Verify the intern's claims by reading the changed files, running `cargo check` / tests / grep, and inspecting actual output. If you didn't verify it, you don't know it.\n\
2. Post a review block in EXACTLY this shape (one block per intern; for `delegate_to_all_interns` produce one block per intern in the order they returned):\n\
\n\
   ## Review · <intern-name>\n\
   - Claimed: <what the intern said it did, one line>\n\
   - Reality: <what you actually found when you verified, one line>\n\
   - Verdict: <one harsh sentence calling out gaps, lies, or what was actually decent>\n\
   - Score: <1-10> — <one-line justification>\n\
\n\
3. Scoring rubric — default toward the LOW end. Most interns score 5-7. A 9 is earned, not given.\n\
   - 10: rare. Task fully done, edge cases handled, verified clean, would ship as-is.\n\
   - 8-9: solid. Done correctly, maybe one tiny nit. Acknowledge briefly, no fanfare.\n\
   - 6-7: passable but lazy. Got the obvious case, missed obvious edges. Call it out.\n\
   - 4-5: partial. Touched the right files but the work is incomplete or has real bugs.\n\
   - 2-3: bad faith — broke something, falsified the report, did the wrong task.\n\
   - 1: complete failure — no useful change, lies throughout.\n\
4. If any score is < 7, list specific concrete issues (file:line where possible) and either fix them yourself or re-delegate with sharper acceptance criteria. Do not paper over a bad result. Do not say \"close enough\".\n\
\n\
Do NOT skip this review — it is part of the response, not optional commentary. If you skip it, you have failed your job as mentor.".to_string());

    if !skills.is_empty() {
        let mut skill_section = "# User-defined Skills\nThe following custom skills are available as slash commands:\n".to_string();
        for (name, desc, when_to_use) in skills {
            skill_section.push_str(&format!(" - /{name}: {desc}\n"));
            if let Some(wtu) = when_to_use {
                skill_section.push_str(&format!("   When to use: {wtu}\n"));
            }
        }
        skill_section.push_str("\nWhen a task matches a skill's purpose, suggest the user invoke it with the slash command.");
        sections.push(skill_section);
    }

    let commit_line = if commit_attribution {
        "  - Commit attribution is allowed (Co-Authored-By trailers, etc.)."
    } else {
        "  - No AI/assistant attribution in commits (no Co-Authored-By, no \"Generated with\")."
    };
    sections.push(format!(
        "# Project-specific rules\n \
 - No code comments of any kind.\n\
{commit_line}\n \
 - Max 200 lines per source file — split before reaching the limit.\n \
 - Single-responsibility files. Low coupling, high cohesion."
    ));

    sections.push(environment_section(env));

    if let Some(ref gs) = env.git_status {
        sections.push(format!("gitStatus: {gs}"));
    }

    let claude_md = load_claude_md(&env.cwd);
    if !claude_md.is_empty() {
        sections.push(format!("# Project Instructions (CLAUDE.md)\n\n{claude_md}"));
    }

    sections.push(format!("# Current Date\nToday's date is {today}."));

    sections.join("\n\n")
}

fn rust_section() -> String {
    "# Rust Project Context
 - Use `cargo check` (not build) for fast feedback. `cargo clippy -- -D warnings` for lints. `cargo fmt` for formatting.
 - Respect the borrow checker. Prefer `?` over `unwrap`. Use `-p <crate>` for workspace targets.
 - Prefer iterators and combinators. Avoid blocking in async contexts.
 - Run `cargo check` after edits to confirm correctness.".to_string()
}

fn is_rust_project(cwd: &str) -> bool {
    let cwd_path = Path::new(cwd);
    if cwd_path.join("Cargo.toml").exists() {
        return true;
    }
    if let Some(parent) = cwd_path.parent()
        && parent.join("Cargo.toml").exists() {
            return true;
        }
    false
}

fn load_claude_md(cwd: &str) -> String {
    let mut contents = Vec::new();
    let cwd_path = Path::new(cwd);
    let home = dirs_or_home();

    let mut dir = Some(cwd_path);
    while let Some(d) = dir {
        for name in &["CLAUDE.md", "MEMORY.md"] {
            let candidate = d.join(name);
            if candidate.is_file()
                && let Ok(text) = std::fs::read_to_string(&candidate) {
                    contents.push(text);
                }
        }
        for name in &["CLAUDE.md", "MEMORY.md"] {
            let dotclaude = d.join(".claude").join(name);
            if dotclaude.is_file()
                && let Ok(text) = std::fs::read_to_string(&dotclaude) {
                    contents.push(text);
                }
        }
        if d == home.as_path() {
            break;
        }
        dir = d.parent();
    }

    let home_dotclaude_claude = home.join(".claude").join("CLAUDE.md");
    let home_dotclaude_memory = home.join(".claude").join("MEMORY.md");
    for path in [home_dotclaude_claude, home_dotclaude_memory] {
        if path.is_file()
            && let Ok(text) = std::fs::read_to_string(&path)
                && !contents.iter().any(|c| c == &text) {
                    contents.push(text);
                }
    }

    contents.join("\n\n---\n\n")
}

fn dirs_or_home() -> std::path::PathBuf {
    stynx_code_config::home_dir()
        .unwrap_or_else(|| std::path::PathBuf::from("."))
}

pub fn build_env_info(cwd: String, model_id: String) -> EnvInfo {
    use super::git::{git_status_snapshot, is_git_repo};

    let platform = std::env::consts::OS.to_string();

    let shell = if cfg!(windows) {
        if std::env::var("PSModulePath").is_ok() {
            "powershell".to_string()
        } else {
            std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string())
        }
    } else {
        std::env::var("SHELL")
            .map(|s| {
                if s.contains("zsh") { "zsh".to_string() }
                else if s.contains("bash") { "bash".to_string() }
                else { s }
            })
            .unwrap_or_else(|_| "unknown".to_string())
    };

    let os_version = if cfg!(windows) {
        std::process::Command::new("cmd")
            .args(["/C", "ver"])
            .output()
            .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
            .unwrap_or_else(|_| platform.clone())
    } else {
        std::process::Command::new("uname")
            .args(["-sr"])
            .output()
            .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
            .unwrap_or_else(|_| platform.clone())
    };

    let is_git = is_git_repo(&cwd);
    let git_status = if is_git { git_status_snapshot(&cwd) } else { None };

    EnvInfo { cwd, is_git, platform, shell, os_version, model_id, git_status }
}
