use crate::acp_bridge::SseEvent;
use serde_json::{json, Value};
use std::env;

const DEFAULT_API_CHAT_BASE_URL: &str = "http://127.0.0.1:20128/v1";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ApiChatProfile {
    pub profile_id: &'static str,
    pub base_profile: &'static str,
    pub isolated_profile: &'static str,
    pub label: &'static str,
    pub model: &'static str,
    pub provider_kind: &'static str,
    pub status: &'static str,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedApiChatProfile {
    pub profile_id: String,
    pub base_profile: String,
    pub isolated_profile: String,
    pub label: String,
    pub model: String,
    pub provider_kind: String,
    pub status: String,
    pub base_url: String,
    pub api_key: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApiChatError {
    pub code: &'static str,
    pub message: String,
}

impl ApiChatError {
    pub fn new(code: &'static str, message: impl Into<String>) -> Self {
        Self {
            code,
            message: message.into(),
        }
    }
}

impl std::fmt::Display for ApiChatError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}: {}", self.code, self.message)
    }
}

impl std::error::Error for ApiChatError {}

pub const API_CHAT_PROFILES: &[ApiChatProfile] = &[
    ApiChatProfile {
        profile_id: "shared_api_deepseek_flash_chat",
        base_profile: "api-deepseek-flash-chat",
        isolated_profile: "api-deepseek-flash-chat",
        label: "DeepSeek Flash Chat",
        model: "deepseek-v4-flash",
        provider_kind: "api-chat",
        status: "active",
    },
    ApiChatProfile {
        profile_id: "shared_api_deepseek_pro_chat",
        base_profile: "api-deepseek-pro-chat",
        isolated_profile: "api-deepseek-pro-chat",
        label: "DeepSeek Pro Chat",
        model: "deepseek-v4-pro",
        provider_kind: "api-chat",
        status: "active",
    },
    ApiChatProfile {
        profile_id: "shared_api_gpt_chat",
        base_profile: "api-gpt-chat",
        isolated_profile: "api-gpt-chat",
        label: "GPT Chat",
        model: "aisz-chat/gpt-5.5-extra-high-fast",
        provider_kind: "api-chat",
        status: "active",
    },
    ApiChatProfile {
        profile_id: "shared_api_kimi_chat",
        base_profile: "api-kimi-chat",
        isolated_profile: "api-kimi-chat",
        label: "Kimi Chat",
        model: "aisz-chat/kimi-k2.5",
        provider_kind: "api-chat",
        status: "active",
    },
    ApiChatProfile {
        profile_id: "shared_api_gemini_chat",
        base_profile: "api-gemini-chat",
        isolated_profile: "api-gemini-chat",
        label: "Gemini API Chat",
        model: "aisz-chat/gemini-3.1-pro",
        provider_kind: "api-chat",
        status: "active",
    },
    ApiChatProfile {
        profile_id: "shared_api_grok_chat",
        base_profile: "api-grok-chat",
        isolated_profile: "api-grok-chat",
        label: "Grok API Chat",
        model: "aisz-chat/grok-4.3",
        provider_kind: "api-chat",
        status: "active",
    },
];

pub fn api_chat_profiles() -> &'static [ApiChatProfile] {
    API_CHAT_PROFILES
}

pub fn api_chat_profile_by_id(value: &str) -> Option<ApiChatProfile> {
    let needle = value.trim();
    if needle.is_empty() {
        return None;
    }
    API_CHAT_PROFILES
        .iter()
        .copied()
        .find(|profile| profile_matches(*profile, needle))
}

pub fn payload_uses_api_chat_profile(payload: &Value, registration_profile: &str) -> bool {
    let agent_id = payload
        .get("agentId")
        .and_then(Value::as_str)
        .map(str::trim)
        .unwrap_or_default();
    if agent_id != "chat_only" {
        return false;
    }
    let agent_profile = payload.get("agentProfileRef");
    [
        Some(registration_profile),
        payload.get("profile").and_then(Value::as_str),
        payload.get("profileId").and_then(Value::as_str),
        payload.get("profile_id").and_then(Value::as_str),
        agent_profile
            .and_then(|value| value.get("baseProfile"))
            .and_then(Value::as_str),
        agent_profile
            .and_then(|value| value.get("isolatedProfile"))
            .and_then(Value::as_str),
        agent_profile
            .and_then(|value| value.get("profileId"))
            .and_then(Value::as_str),
    ]
    .into_iter()
    .flatten()
    .any(|candidate| api_chat_profile_by_id(candidate).is_some())
}

pub fn resolve_api_chat_profile(value: &str) -> Result<ResolvedApiChatProfile, ApiChatError> {
    let profile = api_chat_profile_by_id(value).ok_or_else(|| {
        ApiChatError::new(
            "api_chat_profile_unknown",
            format!("未知 API Chat profile: {value}"),
        )
    })?;
    let env_prefix = profile_env_prefix(profile.profile_id);
    let model = env_value(&format!("{env_prefix}_MODEL"))
        .or_else(|| env_value("MNOTE_API_CHAT_MODEL"))
        .unwrap_or_else(|| profile.model.to_string());
    let base_url = env_value(&format!("{env_prefix}_BASE_URL"))
        .or_else(|| env_value("MNOTE_API_CHAT_BASE_URL"))
        .unwrap_or_else(|| DEFAULT_API_CHAT_BASE_URL.to_string());
    let api_key = env_value(&format!("{env_prefix}_API_KEY"))
        .or_else(|| env_value("MNOTE_API_CHAT_API_KEY"))
        .or_else(|| env_value("OPENAI_API_KEY"));
    Ok(ResolvedApiChatProfile {
        profile_id: profile.profile_id.to_string(),
        base_profile: profile.base_profile.to_string(),
        isolated_profile: profile.isolated_profile.to_string(),
        label: profile.label.to_string(),
        model,
        provider_kind: profile.provider_kind.to_string(),
        status: profile.status.to_string(),
        base_url: base_url.trim().trim_end_matches('/').to_string(),
        api_key,
    })
}

pub fn runtime_events_from_openai_sse_chunk(
    run_id: &str,
    chunk: &str,
) -> Result<Vec<SseEvent>, ApiChatError> {
    let mut decoder = OpenAiSseDecoder::default();
    decoder.push_chunk(run_id, chunk)
}

#[derive(Debug, Default)]
pub struct OpenAiSseDecoder {
    buffer: String,
    output: String,
    completed: bool,
}

impl OpenAiSseDecoder {
    pub fn push_chunk(&mut self, run_id: &str, chunk: &str) -> Result<Vec<SseEvent>, ApiChatError> {
        self.buffer.push_str(chunk);
        let mut frames = self
            .buffer
            .split("\n\n")
            .map(str::to_string)
            .collect::<Vec<_>>();
        self.buffer = frames.pop().unwrap_or_default();
        let mut events = Vec::new();
        for frame in frames {
            events.extend(self.parse_frame(run_id, &frame)?);
        }
        Ok(events)
    }

    pub fn finish(&mut self, run_id: &str) -> Result<Vec<SseEvent>, ApiChatError> {
        let rest = std::mem::take(&mut self.buffer);
        let mut events = if rest.trim().is_empty() {
            Vec::new()
        } else {
            self.parse_frame(run_id, &rest)?
        };
        if !self.completed {
            self.completed = true;
            events.push(self.completed_event());
        }
        Ok(events)
    }

    fn parse_frame(&mut self, run_id: &str, frame: &str) -> Result<Vec<SseEvent>, ApiChatError> {
        let mut events = Vec::new();
        for data in sse_frame_data_lines(frame) {
            if data == "[DONE]" {
                if !self.completed {
                    self.completed = true;
                    events.push(self.completed_event());
                }
                continue;
            }
            let payload = serde_json::from_str::<Value>(&data).map_err(|error| {
                ApiChatError::new(
                    "api_chat_stream_parse_error",
                    format!("OpenAI SSE chunk 解析失败: {error}"),
                )
            })?;
            if let Some(error) = payload.get("error") {
                self.completed = true;
                events.push(SseEvent {
                    event: "run.failed".into(),
                    data: json!({
                        "runId": run_id,
                        "code": "api_chat_upstream_error",
                        "message": error
                            .get("message")
                            .and_then(Value::as_str)
                            .unwrap_or("API Chat upstream error"),
                        "error": error
                    }),
                });
                continue;
            }
            for delta in extract_delta_texts(&payload) {
                self.output.push_str(&delta);
                events.push(SseEvent {
                    event: "message.delta".into(),
                    data: json!({
                        "runId": run_id,
                        "delta": delta
                    }),
                });
            }
        }
        Ok(events)
    }

    fn completed_event(&self) -> SseEvent {
        SseEvent {
            event: "run.completed".into(),
            data: json!({
                "output": self.output
            }),
        }
    }
}

fn profile_matches(profile: ApiChatProfile, value: &str) -> bool {
    profile.profile_id == value
        || profile.base_profile == value
        || profile.isolated_profile == value
        || profile.label == value
}

fn profile_env_prefix(profile_id: &str) -> String {
    let suffix = profile_id
        .trim()
        .strip_prefix("shared_api_")
        .unwrap_or(profile_id)
        .chars()
        .map(|ch| {
            if ch.is_ascii_alphanumeric() {
                ch.to_ascii_uppercase()
            } else {
                '_'
            }
        })
        .collect::<String>();
    format!("MNOTE_API_CHAT_{suffix}")
}

fn env_value(key: &str) -> Option<String> {
    env::var(key)
        .ok()
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
}

fn sse_frame_data_lines(frame: &str) -> Vec<String> {
    let mut lines = Vec::new();
    for line in frame.lines() {
        let trimmed = line.trim();
        if let Some(data) = trimmed.strip_prefix("data:") {
            lines.push(data.trim().to_string());
        }
    }
    lines
}

fn extract_delta_texts(payload: &Value) -> Vec<String> {
    let mut texts = Vec::new();
    if let Some(choices) = payload.get("choices").and_then(Value::as_array) {
        for choice in choices {
            for value in [
                choice
                    .get("delta")
                    .and_then(|delta| delta.get("content"))
                    .and_then(Value::as_str),
                choice
                    .get("message")
                    .and_then(|message| message.get("content"))
                    .and_then(Value::as_str),
                choice.get("text").and_then(Value::as_str),
            ]
            .into_iter()
            .flatten()
            {
                if !value.is_empty() {
                    texts.push(value.to_string());
                }
            }
        }
    }
    if texts.is_empty() {
        for value in [
            payload.get("delta").and_then(Value::as_str),
            payload.get("text").and_then(Value::as_str),
            payload.get("content").and_then(Value::as_str),
        ]
        .into_iter()
        .flatten()
        {
            if !value.is_empty() {
                texts.push(value.to_string());
            }
        }
    }
    texts
}
