use compliance_core::error::CoreError;
use compliance_core::models::{TrackerIssue, TrackerType};
use compliance_core::traits::issue_tracker::{IssueTracker, ReviewComment};
use secrecy::{ExposeSecret, SecretString};

pub struct JiraTracker {
    base_url: String,
    email: String,
    api_token: SecretString,
    project_key: String,
    http: reqwest::Client,
}

impl JiraTracker {
    pub fn new(
        base_url: String,
        email: String,
        api_token: SecretString,
        project_key: String,
    ) -> Self {
        Self {
            base_url: base_url.trim_end_matches('/').to_string(),
            email,
            api_token,
            project_key,
            http: reqwest::Client::new(),
        }
    }

    fn auth_header(&self) -> String {
        use base64::Engine;
        let credentials = format!("{}:{}", self.email, self.api_token.expose_secret());
        format!(
            "Basic {}",
            base64::engine::general_purpose::STANDARD.encode(credentials)
        )
    }
}

impl IssueTracker for JiraTracker {
    fn name(&self) -> &str {
        "jira"
    }

    async fn create_issue(
        &self,
        _owner: &str,
        _repo: &str,
        title: &str,
        body: &str,
        labels: &[String],
    ) -> Result<TrackerIssue, CoreError> {
        let url = format!("{}/rest/api/3/issue", self.base_url);

        let mut payload = serde_json::json!({
            "fields": {
                "project": { "key": self.project_key },
                "summary": title,
                "description": {
                    "type": "doc",
                    "version": 1,
                    "content": [{
                        "type": "paragraph",
                        "content": [{
                            "type": "text",
                            "text": body,
                        }]
                    }]
                },
                "issuetype": { "name": "Bug" },
            }
        });

        if !labels.is_empty() {
            payload["fields"]["labels"] = serde_json::Value::Array(
                labels
                    .iter()
                    .map(|l| serde_json::Value::String(l.clone()))
                    .collect(),
            );
        }

        let resp = self
            .http
            .post(&url)
            .header("Authorization", self.auth_header())
            .header("Content-Type", "application/json")
            .json(&payload)
            .send()
            .await
            .map_err(|e| CoreError::IssueTracker(format!("Jira create issue failed: {e}")))?;

        if !resp.status().is_success() {
            let status = resp.status();
            let body = resp.text().await.unwrap_or_default();
            return Err(CoreError::IssueTracker(format!(
                "Jira returned {status}: {body}"
            )));
        }

        let issue: serde_json::Value = resp
            .json()
            .await
            .map_err(|e| CoreError::IssueTracker(format!("Failed to parse Jira response: {e}")))?;

        let key = issue["key"].as_str().unwrap_or("").to_string();
        let url = format!("{}/browse/{}", self.base_url, key);

        Ok(TrackerIssue::new(
            String::new(),
            TrackerType::Jira,
            key,
            url,
            title.to_string(),
        ))
    }

    async fn update_issue_status(
        &self,
        _owner: &str,
        _repo: &str,
        external_id: &str,
        status: &str,
    ) -> Result<(), CoreError> {
        // Get available transitions
        let url = format!(
            "{}/rest/api/3/issue/{external_id}/transitions",
            self.base_url
        );
        let resp = self
            .http
            .get(&url)
            .header("Authorization", self.auth_header())
            .send()
            .await
            .map_err(|e| CoreError::IssueTracker(format!("Jira get transitions failed: {e}")))?;

        let body: serde_json::Value = resp.json().await.unwrap_or_default();
        let transitions = body["transitions"].as_array();

        // Find matching transition
        if let Some(transitions) = transitions {
            let target = match status {
                "closed" | "resolved" => "Done",
                "in_progress" => "In Progress",
                _ => "To Do",
            };

            if let Some(transition) = transitions.iter().find(|t| {
                t["name"]
                    .as_str()
                    .map(|n| n.eq_ignore_ascii_case(target))
                    .unwrap_or(false)
            }) {
                let transition_id = transition["id"].as_str().unwrap_or_default();
                self.http
                    .post(format!(
                        "{}/rest/api/3/issue/{external_id}/transitions",
                        self.base_url
                    ))
                    .header("Authorization", self.auth_header())
                    .json(&serde_json::json!({ "transition": { "id": transition_id } }))
                    .send()
                    .await
                    .map_err(|e| CoreError::IssueTracker(format!("Jira transition failed: {e}")))?;
            }
        }

        Ok(())
    }

    async fn add_comment(
        &self,
        _owner: &str,
        _repo: &str,
        external_id: &str,
        body: &str,
    ) -> Result<(), CoreError> {
        let url = format!("{}/rest/api/3/issue/{external_id}/comment", self.base_url);

        self.http
            .post(&url)
            .header("Authorization", self.auth_header())
            .header("Content-Type", "application/json")
            .json(&serde_json::json!({
                "body": {
                    "type": "doc",
                    "version": 1,
                    "content": [{
                        "type": "paragraph",
                        "content": [{
                            "type": "text",
                            "text": body,
                        }]
                    }]
                }
            }))
            .send()
            .await
            .map_err(|e| CoreError::IssueTracker(format!("Jira add comment failed: {e}")))?;

        Ok(())
    }

    async fn create_pr_review(
        &self,
        _owner: &str,
        _repo: &str,
        _pr_number: u64,
        _body: &str,
        _comments: Vec<ReviewComment>,
    ) -> Result<(), CoreError> {
        // Jira doesn't have native PR reviews - this is a no-op
        tracing::info!("Jira doesn't support PR reviews natively, skipping");
        Ok(())
    }

    async fn find_existing_issue(
        &self,
        _owner: &str,
        _repo: &str,
        fingerprint: &str,
    ) -> Result<Option<TrackerIssue>, CoreError> {
        let jql = format!(
            "project = {} AND text ~ \"{}\"",
            self.project_key, fingerprint
        );
        let url = format!("{}/rest/api/3/search", self.base_url);

        let resp = self
            .http
            .get(&url)
            .header("Authorization", self.auth_header())
            .query(&[("jql", &jql), ("maxResults", &"1".to_string())])
            .send()
            .await
            .map_err(|e| CoreError::IssueTracker(format!("Jira search failed: {e}")))?;

        let body: serde_json::Value = resp.json().await.unwrap_or_default();
        if let Some(issue) = body["issues"].as_array().and_then(|arr| arr.first()) {
            let key = issue["key"].as_str().unwrap_or("").to_string();
            let url = format!("{}/browse/{}", self.base_url, key);
            let title = issue["fields"]["summary"]
                .as_str()
                .unwrap_or("")
                .to_string();
            Ok(Some(TrackerIssue::new(
                String::new(),
                TrackerType::Jira,
                key,
                url,
                title,
            )))
        } else {
            Ok(None)
        }
    }
}
