use serde::Deserialize;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

/// Mirrors the relevant fields of capacitor.config.json.
#[derive(Debug, Deserialize)]
struct CapacitorConfig {
    #[serde(rename = "webDir")]
    web_dir: String,
}

const RAW_FILE_TARGET: &str = "openharmony/entry/src/main/resources/rawfile/www";
const BRIDGE_FILES: &[&str] = &["cordova.js", "native-bridge.js", "cordova_plugins.js"];

/// Locate the project root by scanning upward for `capacitor.config.json`.
fn find_project_root() -> Option<PathBuf> {
    let cwd = env::current_dir().ok()?;
    for ancestor in cwd.ancestors() {
        if ancestor.join("capacitor.config.json").exists() {
            return Some(ancestor.to_path_buf());
        }
    }
    None
}

/// Read and parse capacitor.config.json.
fn read_config(root: &Path) -> Result<CapacitorConfig, String> {
    let path = root.join("capacitor.config.json");
    let content = fs::read_to_string(&path)
        .map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
    serde_json::from_str(&content)
        .map_err(|e| format!("Failed to parse capacitor.config.json: {}", e))
}

/// Find the Vite build output directory.
/// Usually `dist/` or whatever vite.config.ts / package.json defines.
fn find_vite_out_dir(root: &Path) -> PathBuf {
    // Default: dist/
    let dist = root.join("dist");
    if dist.is_dir() {
        return dist;
    }
    // Fallback: try www/
    let www = root.join("www");
    if www.is_dir() {
        return www;
    }
    dist // return default path even if it doesn't exist
}

/// Find the OHOS rawfile target directory.
fn ohos_rawfile_dir(root: &Path) -> PathBuf {
    root.join(RAW_FILE_TARGET)
}

/// Copy directory contents recursively (simpler than walkdir for this use-case).
fn copy_dir(src: &Path, dst: &Path) -> Result<(), String> {
    if !src.is_dir() {
        return Err(format!("Source directory does not exist: {}", src.display()));
    }
    fs::create_dir_all(dst)
        .map_err(|e| format!("Failed to create {}: {}", dst.display(), e))?;

    for entry in fs::read_dir(src).map_err(|e| format!("Failed to read {}: {}", src.display(), e))? {
        let entry = entry.map_err(|e| format!("Dir entry error: {}", e))?;
        let file_type = entry.file_type().map_err(|e| format!("File type error: {}", e))?;
        let src_path = entry.path();
        let name = entry.file_name();
        let dst_path = dst.join(&name);

        if file_type.is_dir() {
            copy_dir(&src_path, &dst_path)?;
        } else {
            fs::copy(&src_path, &dst_path)
                .map_err(|e| format!("Failed to copy {} -> {}: {}", src_path.display(), dst_path.display(), e))?;
        }
    }
    Ok(())
}

/// Check if a bridge file exists in the given directory.
fn has_bridge_file(dir: &Path, name: &str) -> bool {
    dir.join(name).exists()
}

/// Copy a bridge file from node_modules/@capacitor-ohos to the target directory.
fn copy_bridge_from_node_modules(root: &Path, target: &Path, name: &str) -> Result<bool, String> {
    let source = root
        .join("node_modules")
        .join("@capacitor-ohos")
        .join("ohos")
        .join("src")
        .join("main")
        .join("resources")
        .join("rawfile")
        .join(name);

    if source.exists() {
        fs::copy(&source, target.join(name))
            .map_err(|e| format!("Failed to copy bridge file {}: {}", source.display(), e))?;
        Ok(true)
    } else {
        Ok(false)
    }
}

/// Try to restore a bridge file from git history.
fn restore_from_git(root: &Path, target: &Path, name: &str) -> Result<bool, String> {
    let rawfile_path = format!("openharmony/entry/src/main/resources/rawfile/www/{}", name);
    let output = std::process::Command::new("git")
        .args(["show", &format!("HEAD:{}", rawfile_path)])
        .current_dir(root)
        .output()
        .map_err(|e| format!("Failed to run git show: {}", e))?;

    if output.status.success() {
        fs::write(target.join(name), &output.stdout)
            .map_err(|e| format!("Failed to write restored file {}: {}", name, e))?;
        Ok(true)
    } else {
        Ok(false)
    }
}

/// Ensure all bridge files are present in the target directory.
fn ensure_bridge_files(root: &Path, target: &Path) -> Result<(), String> {
    for &name in BRIDGE_FILES {
        if has_bridge_file(target, name) {
            println!("  ✓ {} (already present)", name);
            continue;
        }

        // Try 1: git restore from HEAD
        if restore_from_git(root, target, name)? {
            println!("  ✓ {} (restored from git)", name);
            continue;
        }

        // Try 2: copy from node_modules/@capacitor-ohos
        if copy_bridge_from_node_modules(root, target, name)? {
            println!("  ✓ {} (copied from node_modules)", name);
            continue;
        }

        // Missing – the app might not need it, but warn.
        println!("  ⚠ {} not found (skipped – may be optional)", name);
    }
    Ok(())
}

fn main() {
    let root = find_project_root().unwrap_or_else(|| {
        eprintln!("Error: cannot find capacitor.config.json in any parent directory.");
        std::process::exit(1);
    });

    println!("Project root: {}", root.display());
    println!();

    // ── Step 1: Read capacitor.config.json ──
    let cfg = match read_config(&root) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1);
        }
    };
    println!("Config: webDir = \"{}\"", cfg.web_dir);

    // ── Step 2: Detect Vite build output ──
    let vite_out = find_vite_out_dir(&root);
    if !vite_out.exists() {
        eprintln!(
            "Error: Vite build output not found at \"{}\".\n\
             Run `npm run build` or `hionic buildui` first.",
            vite_out.display()
        );
        std::process::exit(1);
    }
    println!("Vite build output: {}", vite_out.display());

    // Detect mismatch: if webDir doesn't match the actual Vite output directory
    let vite_out_name = vite_out
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("dist");
    if cfg.web_dir != vite_out_name {
        println!(
            "  ⚠ Mismatch: capacitor.config.json has webDir = \"{}\" but Vite builds to \"{}\".",
            cfg.web_dir, vite_out_name
        );
        println!(
            "  → Suggestion: change webDir to \"{}\" in capacitor.config.json",
            vite_out_name
        );
    }

    // ── Step 3: Clear and copy to OHOS rawfile ──
    let target = ohos_rawfile_dir(&root);
    println!("Target: {}", target.display());

    if target.exists() {
        fs::remove_dir_all(&target)
            .unwrap_or_else(|e| {
                eprintln!("Warning: failed to remove old rawfile/www: {}", e);
            });
    }
    fs::create_dir_all(&target)
        .unwrap_or_else(|e| {
            eprintln!("Error: failed to create {}: {}", target.display(), e);
            std::process::exit(1);
        });

    println!("\nCopying web assets...");
    if let Err(e) = copy_dir(&vite_out, &target) {
        eprintln!("Error: {}", e);
        std::process::exit(1);
    }

    // ── Step 4: Ensure bridge files ──
    println!("\nChecking bridge files...");
    if let Err(e) = ensure_bridge_files(&root, &target) {
        eprintln!("Error: {}", e);
        std::process::exit(1);
    }

    // ── Summary ──
    println!("\n── Sync complete ──");
    let file_count = match fs::read_dir(&target) {
        Ok(entries) => entries.count(),
        Err(_) => 0,
    };
    println!("Files deployed to {}: {}", target.display(), file_count);
    println!("\nNext steps:");
    println!("  1. cd openharmony");
    println!("  2. hvigorw assembleHap");
    println!("  3. hionic run openharmony");
}