use std::str::FromStr;

use anyhow::Result;
use colored::Colorize;

use crate::api::{
    PlaneApiClient, PlaneState, PlaneWorkItem, ProjectId, ScheduledDate, StateGroup,
    WorkItemId, WorkItemPriority,
};
use crate::config::resolve_plane_session;

/// Validated Plane web URL that has been checked for the expected host prefix.
/// Parsing fails for URLs that don't begin with `https://app.plane.so/`.
#[derive(Clone)]
pub struct PlaneWebUrl(String);

/// Validated construction from a raw URL string
impl FromStr for PlaneWebUrl {
    type Err = anyhow::Error;

    fn from_str(raw_url: &str) -> Result<Self> {
        if !raw_url.starts_with("https://app.plane.so/")
            && !raw_url.starts_with("http://app.plane.so/")
        {
            anyhow::bail!("not a Plane URL — expected https://app.plane.so/...");
        }
        Ok(Self(raw_url.to_owned()))
    }
}

/// String access for display and further parsing
impl PlaneWebUrl {
    /// Returns the raw URL string for display and path segment extraction
    fn as_str(&self) -> &str {
        &self.0
    }
}

/// Parses a Plane web URL and prints a summary of the referenced entity.
/// Accepts URLs in the format `https://app.plane.so/{workspace}/projects/{id}/issues/{id}`.
/// Usage: `plane info https://app.plane.so/jakutainc/projects/.../issues/...`
#[derive(clap::Args)]
pub struct InspectUrlArgs {
    /// Plane web URL to parse (e.g. from browser address bar or Slack message)
    plane_url: PlaneWebUrl,
}

/// The entity type and typed IDs extracted from a parsed Plane URL
enum ParsedPlaneEntity {
    /// A specific work item identified by validated project and issue UUIDs
    WorkItem(PlaneUrlWorkItemRef),
    /// The issues list view for a project (no specific work item selected)
    ProjectIssues(ProjectId),
}

/// Validated project and work item UUIDs extracted from a Plane work item URL
struct PlaneUrlWorkItemRef {
    project_id: ProjectId,
    work_item_id: WorkItemId,
}

/// Execution logic for the `plane info` subcommand
impl InspectUrlArgs {
    /// Parses the URL, fetches the referenced entity from the Plane API,
    /// and prints a summary to the terminal
    pub async fn execute(self) -> Result<()> {
        let parsed_entity = parse_plane_url(&self.plane_url)?;

        let plane_session = resolve_plane_session()?;
        let api_client = PlaneApiClient::try_new(plane_session)?;

        match parsed_entity {
            ParsedPlaneEntity::WorkItem(work_item_ref) => {
                let project_client = api_client.project_operations(&work_item_ref.project_id);
                let item_client =
                    project_client.work_item_operations(&work_item_ref.work_item_id);
                let fetched_item = item_client.fetch_detail().await?;

                let project_states =
                    api_client.list_states(&work_item_ref.project_id).await?;
                let projects = api_client.list_projects().await?;

                let mut project_identifier_code = String::from("???");
                for candidate_project in &projects {
                    if candidate_project.id().as_str()
                        == work_item_ref.project_id.as_str()
                    {
                        project_identifier_code =
                            candidate_project.identifier().to_owned();
                    }
                }

                let detail_printer = UrlEntityPrinter {
                    project_states: &project_states,
                    project_identifier_code: &project_identifier_code,
                };
                detail_printer.print_work_item(&fetched_item);
            }
            ParsedPlaneEntity::ProjectIssues(project_id) => {
                let projects = api_client.list_projects().await?;

                let mut project_display_name = String::from("Unknown");
                let mut project_identifier_code = String::from("???");
                for candidate_project in &projects {
                    if candidate_project.id().as_str() == project_id.as_str() {
                        project_display_name =
                            candidate_project.display_name().to_owned();
                        project_identifier_code =
                            candidate_project.identifier().to_owned();
                    }
                }

                println!(
                    "{} ({})",
                    project_display_name.bold(),
                    project_identifier_code
                );
                println!("  URL: {}", self.plane_url.as_str());
            }
        }

        Ok(())
    }
}

/// Splits a Plane web URL into its path segments and extracts the entity type
/// and validated UUIDs. Returns an error if the URL structure is unrecognized
/// or the embedded UUIDs fail validation.
fn parse_plane_url(plane_url: &PlaneWebUrl) -> Result<ParsedPlaneEntity> {
    let url_text = plane_url.as_str();
    let path_remainder = url_text
        .strip_prefix("https://app.plane.so/")
        .or_else(|| url_text.strip_prefix("http://app.plane.so/"));

    let url_path = match path_remainder {
        Option::Some(path_text) => path_text,
        Option::None => {
            anyhow::bail!("not a Plane URL — expected https://app.plane.so/...");
        }
    };

    let path_segments: Vec<&str> = url_path.trim_end_matches('/').split('/').collect();

    if path_segments.len() < 3 {
        anyhow::bail!(
            "URL too short — expected at least workspace/projects/project-id"
        );
    }

    let projects_segment = match path_segments.get(1) {
        Option::Some(segment_text) => *segment_text,
        Option::None => {
            anyhow::bail!("URL missing path segments after workspace");
        }
    };

    if projects_segment != "projects" {
        anyhow::bail!(
            "unexpected URL structure — expected 'projects' segment, got '{}'",
            projects_segment
        );
    }

    let project_uuid_text = match path_segments.get(2) {
        Option::Some(uuid_text) => *uuid_text,
        Option::None => {
            anyhow::bail!("URL missing project UUID after /projects/");
        }
    };

    let project_id: ProjectId = project_uuid_text.parse()?;

    let entity_type_segment = path_segments.get(3).copied();
    let entity_id_segment = path_segments.get(4).copied();

    match (entity_type_segment, entity_id_segment) {
        (Option::Some("issues"), Option::Some(issue_uuid_text)) => {
            let work_item_id: WorkItemId = issue_uuid_text.parse()?;
            Ok(ParsedPlaneEntity::WorkItem(PlaneUrlWorkItemRef {
                project_id,
                work_item_id,
            }))
        }
        (Option::Some("issues"), Option::None) => {
            Ok(ParsedPlaneEntity::ProjectIssues(project_id))
        }
        (Option::None, _) => {
            Ok(ParsedPlaneEntity::ProjectIssues(project_id))
        }
        (Option::Some(unknown_segment), _) => {
            anyhow::bail!(
                "unsupported entity type '{}' — supported: issues",
                unknown_segment
            );
        }
    }
}

/// Handles terminal output for entities fetched via URL inspection
struct UrlEntityPrinter<'a> {
    project_states: &'a [PlaneState],
    project_identifier_code: &'a str,
}

/// Work item detail rendering for the URL inspection command
impl<'a> UrlEntityPrinter<'a> {
    /// Prints a work item summary with identifier, state, priority, and due date
    fn print_work_item(&self, work_item: &PlaneWorkItem) {
        let identifier_display = format!(
            "{}-{}",
            self.project_identifier_code,
            work_item.sequence_id().as_u32()
        );

        let resolved_state_display = self.resolve_state_display(work_item);
        let priority_display = format_priority(work_item.priority());

        println!(
            "{} {}",
            identifier_display.bold(),
            work_item.title().as_str().bold()
        );
        println!("  State:     {}", resolved_state_display);
        println!("  Priority:  {}", priority_display);

        match work_item.target_date() {
            ScheduledDate::Scheduled(date_text) => {
                println!("  Due:       {}", date_text);
            }
            ScheduledDate::Unscheduled => {}
        }

        println!("  Assignees: {}", work_item.assignee_ids().len());
        println!("  Created:   {}", work_item.created_at());
    }

    fn resolve_state_display(&self, target_work_item: &PlaneWorkItem) -> String {
        for candidate_state in self.project_states {
            if candidate_state.id() == target_work_item.workflow_state_id() {
                return colorize_state_label(candidate_state);
            }
        }
        target_work_item.workflow_state_id().to_string()
    }
}

fn colorize_state_label(resolved_state: &PlaneState) -> String {
    match resolved_state.state_group() {
        StateGroup::Backlog => resolved_state.display_name().dimmed().to_string(),
        StateGroup::Unstarted => resolved_state.display_name().white().to_string(),
        StateGroup::Started => resolved_state.display_name().yellow().to_string(),
        StateGroup::Completed => resolved_state.display_name().green().to_string(),
        StateGroup::Cancelled => resolved_state.display_name().red().to_string(),
    }
}

fn format_priority(work_item_priority: WorkItemPriority) -> String {
    match work_item_priority {
        WorkItemPriority::Urgent => "Urgent".red().bold().to_string(),
        WorkItemPriority::High => "High".red().to_string(),
        WorkItemPriority::Medium => "Medium".yellow().to_string(),
        WorkItemPriority::Low => "Low".dimmed().to_string(),
        WorkItemPriority::None => String::from("—"),
    }
}
