use std::{collections::HashMap, time::Duration};

use reqwest::{header::HeaderMap, StatusCode};
use serde_json::Value;

use super::RequestProtocol;

#[derive(Debug, Clone)]
pub struct ExecutionArtifact {
    pub protocol: RequestProtocol,
    pub status: StatusCode,
    pub headers: HeaderMap,
    pub body: String,
    pub json: Option<Value>,
    pub metadata: ArtifactMetadata,
    pub timing_phases: Vec<ArtifactTimingPhase>,
    pub transcripts: Vec<ArtifactTranscript>,
    pub retained_artifacts: Vec<RetainedArtifact>,
}

#[derive(Debug, Clone)]
pub enum ArtifactMetadata {
    Http(HttpArtifactMetadata),
}

#[derive(Debug, Clone)]
pub struct HttpArtifactMetadata {
    pub status: StatusCode,
    pub headers: HeaderMap,
}

#[derive(Debug, Clone)]
pub struct ArtifactTimingPhase {
    pub name: String,
    pub duration: Duration,
}

impl ArtifactTimingPhase {
    pub fn new(name: impl Into<String>, duration: Duration) -> Self {
        Self {
            name: name.into(),
            duration,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArtifactTranscriptDirection {
    Outgoing,
    Incoming,
}

#[derive(Debug, Clone)]
pub struct ArtifactTranscript {
    pub direction: ArtifactTranscriptDirection,
    pub label: String,
    pub body: String,
    pub attributes: HashMap<String, String>,
}

#[derive(Debug, Clone)]
pub struct RetainedArtifact {
    pub name: String,
    pub content_type: String,
    pub body: String,
}

impl ExecutionArtifact {
    pub fn new_http(
        status: StatusCode,
        headers: HeaderMap,
        body: String,
        json: Option<Value>,
        transcripts: Vec<ArtifactTranscript>,
    ) -> Self {
        Self {
            protocol: RequestProtocol::Http,
            status,
            headers: headers.clone(),
            body,
            json,
            metadata: ArtifactMetadata::Http(HttpArtifactMetadata { status, headers }),
            timing_phases: Vec::new(),
            transcripts,
            retained_artifacts: Vec::new(),
        }
    }

    pub fn http_status(&self) -> Option<StatusCode> {
        Some(self.status)
    }

    pub fn http_headers(&self) -> Option<&HeaderMap> {
        Some(&self.headers)
    }

    pub fn body_text(&self) -> &str {
        &self.body
    }

    pub fn json_body(&self) -> Option<&Value> {
        self.json.as_ref()
    }
}

impl Default for ExecutionArtifact {
    fn default() -> Self {
        Self::new_http(StatusCode::OK, HeaderMap::new(), String::new(), None, Vec::new())
    }
}