use std::path::Path;
use std::process::ExitCode;

use anyhow::Result;
use serde::Serialize;

use crate::handoff::{self, BranchMode};
use crate::output::CommandReport;
use crate::paths::state::StateLayout;
use crate::profile;
use crate::repo::marker as repo_marker;
use crate::state::escalation as escalation_state;
use crate::state::radar;
use crate::state::runtime as runtime_state;
use crate::state::session as session_state;
use crate::telemetry::{cost as telemetry_cost, host as host_telemetry, host_loop};

#[derive(Serialize)]
pub(crate) struct TelemetryReport {
    command: &'static str,
    ok: bool,
    path: String,
    profile: String,
    host_loop: host_loop::HostLoopReportView,
    prompt_cost: PromptCostReportView,
    #[serde(skip_serializing_if = "Option::is_none")]
    projection_telemetry: Option<radar::ProjectionTelemetryView>,
}

#[derive(Serialize)]
struct PromptCostReportView {
    #[serde(skip_serializing_if = "Option::is_none")]
    host: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    observed_at_epoch_s: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    total_tokens: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    context_window_tokens: Option<u64>,
    cost: telemetry_cost::TelemetryCostView,
}

impl CommandReport for TelemetryReport {
    fn exit_code(&self) -> ExitCode {
        ExitCode::SUCCESS
    }

    fn render_text(&self) {
        println!("Host loop telemetry: {}", self.host_loop.status);
        if let Some(reason) = &self.host_loop.reason {
            println!("{reason}");
        }
        println!(
            "Context emission count: {}",
            self.host_loop.summary.context_emission_count
        );
        if let Some(p95_chars) = self.host_loop.summary.p95_chars {
            println!("P95 context chars: {p95_chars}");
        }
        if let Some(max_chars) = self.host_loop.summary.max_chars {
            println!("Max context chars: {max_chars}");
        }
        println!("Prompt cost status: {}", self.prompt_cost.cost.status);
        if let Some(reason) = &self.prompt_cost.cost.reason {
            println!("{reason}");
        }
    }
}

pub(crate) fn run_report(
    repo_root: &Path,
    explicit_profile: Option<&str>,
    limit: usize,
) -> Result<TelemetryReport> {
    let profile = profile::resolve(explicit_profile)?;
    let layout = StateLayout::resolve(repo_root, profile.clone())?;
    let host_loop = host_loop::build_report(&layout.state_db_path(), limit)?;
    let host_snapshot = host_telemetry::current(repo_root).ok().flatten();
    let (prompt_cost, projection_telemetry) = match repo_marker::load(repo_root)? {
        Some(marker) => {
            let git = handoff::read_git_state(repo_root, BranchMode::AllowDetachedHead).ok();
            let active_session_id = session_state::load_session_id(&layout)?;
            let runtime = runtime_state::load_runtime_state(repo_root, &layout, &marker.locality_id)?;
            let tracked_session = session_state::load_for_layout(&layout)?;
            let tracked_activity = session_state::load_activity_for_layout(&layout)?;
            let session_state_view = radar::build_session_state_view(
                &layout,
                tracked_session.as_ref(),
                tracked_activity.as_ref(),
            );
            let escalation_entries = escalation_state::load_for_layout(&layout)?;
            let escalation_view = escalation_state::build_view(&layout, &escalation_entries);
            let recovery = runtime_state::recovery_view(&runtime.recovery);
            let continuity_actions = runtime
                .state
                .handoff
                .immediate_actions
                .iter()
                .filter(|item| item.lifecycle.is_active())
                .map(|item| item.text.clone())
                .collect::<Vec<_>>();
            let focus = telemetry_cost::continuity_target(
                runtime.execution_gates.view.attention_anchor.as_ref(),
                &runtime.state.handoff.title,
                &continuity_actions,
            );
            let (projection_telemetry, _, _) = radar::build_projection_telemetry(
                &layout,
                None,
                &runtime,
                &runtime.execution_gates.view,
                &escalation_view,
                &recovery,
                &git,
                &session_state_view,
                active_session_id.as_deref(),
                host_snapshot.as_ref(),
                crate::state::compiled::ProjectionTarget::Session,
            )?;
            (
                PromptCostReportView {
                    host: host_snapshot.as_ref().map(|snapshot| snapshot.host),
                    observed_at_epoch_s: host_snapshot
                        .as_ref()
                        .map(|snapshot| snapshot.observed_at_epoch_s),
                    total_tokens: host_snapshot.as_ref().and_then(|snapshot| snapshot.total_tokens),
                    context_window_tokens: host_snapshot
                        .as_ref()
                        .and_then(|snapshot| snapshot.model_context_window),
                    cost: telemetry_cost::build_cost_view_for_focus(
                        &layout,
                        &marker.locality_id,
                        active_session_id.as_deref(),
                        host_snapshot.as_ref(),
                        focus.as_ref(),
                    )?,
                },
                Some(projection_telemetry),
            )
        }
        None => (
            PromptCostReportView {
                host: host_snapshot.as_ref().map(|snapshot| snapshot.host),
                observed_at_epoch_s: host_snapshot
                    .as_ref()
                    .map(|snapshot| snapshot.observed_at_epoch_s),
                total_tokens: host_snapshot.as_ref().and_then(|snapshot| snapshot.total_tokens),
                context_window_tokens: host_snapshot
                    .as_ref()
                    .and_then(|snapshot| snapshot.model_context_window),
                cost: telemetry_cost::TelemetryCostView::unavailable(
                    "missing_locality",
                    "workspace is not linked to a CCD project, so prompt-cost focus analysis is unavailable",
                ),
            },
            None,
        ),
    };

    Ok(TelemetryReport {
        command: "telemetry-report",
        ok: true,
        path: repo_root.display().to_string(),
        profile: profile.to_string(),
        host_loop,
        prompt_cost,
        projection_telemetry,
    })
}
