// SPDX-License-Identifier: MIT
// Copyright (c) 2025-2026 SenWeaverCoding
// Licensed under the MIT License.

use crate::tools::traits::{Tool, ToolResult};
use async_trait::async_trait;
use serde_json::{Value, json};

pub struct ArduinoUploadTool {

    pub port: String,
}

impl ArduinoUploadTool {
    pub fn new(port: String) -> Self {
        Self { port }
    }
}

#[async_trait]
impl Tool for ArduinoUploadTool {
    fn name(&self) -> &str {
        "arduino_upload"
    }

    fn description(&self) -> &str {
        "Generate Arduino sketch code and upload it to the connected Arduino. Use when: user asks to 'make a heart', 'blink LED', or run any custom pattern on Arduino. You MUST write the full .ino sketch code (setup + loop). Arduino Uno: pin 13 = built-in LED. Saves to temp dir, runs arduino-cli compile and upload. Requires arduino-cli installed."
    }

    fn parameters_schema(&self) -> Value {
        json!({
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "Full Arduino sketch code (complete .ino file content)"
                }
            },
            "required": ["code"]
        })
    }

    async fn execute(&self, args: Value) -> anyhow::Result<ToolResult> {
        let code = args
            .get("code")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'code' parameter"))?;

        if code.trim().is_empty() {
            return Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some("Code cannot be empty".into()),
            });
        }

        if crate::util::hidden_async_command("arduino-cli")
            .arg("version")
            .output()
            .await
            .is_err()
        {
            return Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some(
                    "arduino-cli not found. Install it: https://arduino.github.io/arduino-cli/"
                        .into(),
                ),
            });
        }

        let sketch_name = "sen_sketch";
        let temp_dir = std::env::temp_dir().join(format!("sen_{}", uuid::Uuid::new_v4()));
        let sketch_dir = temp_dir.join(sketch_name);
        let ino_path = sketch_dir.join(format!("{}.ino", sketch_name));

        if let Err(e) = tokio::fs::create_dir_all(&sketch_dir).await {
            return Ok(ToolResult {
                success: false,
                output: format!("Failed to create sketch dir: {}", e),
                error: Some(e.to_string()),
            });
        }

        if let Err(e) = tokio::fs::write(&ino_path, code).await {
            let _ = tokio::fs::remove_dir_all(&temp_dir).await;
            return Ok(ToolResult {
                success: false,
                output: format!("Failed to write sketch: {}", e),
                error: Some(e.to_string()),
            });
        }

        let sketch_path = sketch_dir.to_string_lossy();
        let fqbn = "arduino:avr:uno";

        let compile = crate::util::hidden_async_command("arduino-cli")
            .args(["compile", "--fqbn", fqbn, &sketch_path])
            .output()
            .await;

        let compile_output = match compile {
            Ok(o) => o,
            Err(e) => {
                let _ = tokio::fs::remove_dir_all(&temp_dir).await;
                return Ok(ToolResult {
                    success: false,
                    output: format!("arduino-cli compile failed: {}", e),
                    error: Some(e.to_string()),
                });
            }
        };

        if !compile_output.status.success() {
            let stderr = String::from_utf8_lossy(&compile_output.stderr);
            let _ = tokio::fs::remove_dir_all(&temp_dir).await;
            return Ok(ToolResult {
                success: false,
                output: format!("Compile failed:\n{}", stderr),
                error: Some("Arduino compile error".into()),
            });
        }

        let upload = crate::util::hidden_async_command("arduino-cli")
            .args(["upload", "-p", &self.port, "--fqbn", fqbn, &sketch_path])
            .output()
            .await;

        let upload_output = match upload {
            Ok(o) => o,
            Err(e) => {
                let _ = tokio::fs::remove_dir_all(&temp_dir).await;
                return Ok(ToolResult {
                    success: false,
                    output: format!("arduino-cli upload failed: {}", e),
                    error: Some(e.to_string()),
                });
            }
        };

        let _ = tokio::fs::remove_dir_all(&temp_dir).await;

        if !upload_output.status.success() {
            let stderr = String::from_utf8_lossy(&upload_output.stderr);
            return Ok(ToolResult {
                success: false,
                output: format!("Upload failed:\n{}", stderr),
                error: Some("Arduino upload error".into()),
            });
        }

        Ok(ToolResult {
            success: true,
            output:
                "Sketch compiled and uploaded successfully. The Arduino is now running your code."
                    .into(),
            error: None,
        })
    }
}
