use askama::Template;

use super::sorting::SortField;

/// Minimal view-model for a scored task in the "Next Up" table.
pub(in crate::cmd::webui) struct ScoredEntry {
    pub(in crate::cmd::webui) id: String,
    pub(in crate::cmd::webui) short_id: String,
    pub(in crate::cmd::webui) title: String,
    pub(in crate::cmd::webui) score: String,
    pub(in crate::cmd::webui) status: String,
    pub(in crate::cmd::webui) status_display: &'static str,
    /// Pre-formatted equation string for the score tooltip.
    pub(in crate::cmd::webui) equation: String,
    /// Human-friendly unblocks summary for the score tooltip.
    pub(in crate::cmd::webui) unblocks_display: String,
    pub(in crate::cmd::webui) labels: Vec<String>,
}

/// Minimal view-model for a task row in the project task table.
pub(in crate::cmd::webui) struct TaskRow {
    pub(in crate::cmd::webui) full_id: String,
    pub(in crate::cmd::webui) short_id: String,
    pub(in crate::cmd::webui) status: String,
    pub(in crate::cmd::webui) status_display: &'static str,
    pub(in crate::cmd::webui) task_type: String,
    pub(in crate::cmd::webui) priority: String,
    pub(in crate::cmd::webui) effort: String,
    pub(in crate::cmd::webui) title: String,
    pub(in crate::cmd::webui) labels: Vec<String>,
    pub(in crate::cmd::webui) created_at: String,
    pub(in crate::cmd::webui) created_at_display: String,
}

/// Sort context for a section's task table. Column headers become clickable
/// links that set sort/order query params namespaced by the section prefix.
pub(in crate::cmd::webui) struct SortContext {
    /// Base URL for sort links (e.g. `/projects/myproj`).
    pub(in crate::cmd::webui) base_href: String,
    /// Query param prefix for this section (e.g. "ip_", "open_", "closed_").
    pub(in crate::cmd::webui) prefix: String,
    /// Current sort field.
    pub(in crate::cmd::webui) field: String,
    /// Current sort order ("asc" or "desc").
    pub(in crate::cmd::webui) order: String,
    /// Full query string for the current page state (all sections' params),
    /// excluding this section's sort/order/page. Used for building links
    /// that preserve other sections' state.
    pub(in crate::cmd::webui) preserve_qs: String,
}

impl SortContext {
    /// Build the href for a column header link. Clicking the currently-active
    /// column toggles direction; clicking a different column uses its default.
    fn column_href(&self, col: &str) -> String {
        let order = if col == self.field {
            // Toggle current direction.
            match self.order.as_str() {
                "asc" => "desc",
                _ => "asc",
            }
        } else {
            // Use the column's sensible default.
            SortField::parse(col)
                .map(|f| f.default_order().as_str())
                .unwrap_or("asc")
        };
        let mut qs = self.preserve_qs.clone();
        if !qs.is_empty() {
            qs.push('&');
        }
        qs.push_str(&format!(
            "{}sort={col}&{}order={order}",
            self.prefix, self.prefix
        ));
        format!("{}?{qs}", self.base_href)
    }

    /// Return the arrow indicator for the active column, or empty string.
    fn arrow(&self, col: &str) -> &str {
        if col == self.field {
            match self.order.as_str() {
                "asc" => " ↑",
                "desc" => " ↓",
                _ => "",
            }
        } else {
            ""
        }
    }
}

/// All the state needed to render one status section (in-progress, open, or
/// closed) on the project page.
pub(in crate::cmd::webui) struct SectionState {
    /// Human-friendly label (e.g. "In progress").
    pub(in crate::cmd::webui) label: &'static str,
    /// Total tasks with this status (unfiltered).
    pub(in crate::cmd::webui) total_count: usize,
    /// Tasks matching current filters.
    pub(in crate::cmd::webui) filtered_count: usize,
    /// Task rows for the current page.
    pub(in crate::cmd::webui) tasks: Vec<TaskRow>,
    /// Sort context for this section's table.
    pub(in crate::cmd::webui) sort_ctx: SortContext,
    /// Current filter values for this section.
    pub(in crate::cmd::webui) filter_priority: Option<String>,
    pub(in crate::cmd::webui) filter_effort: Option<String>,
    pub(in crate::cmd::webui) filter_label: Option<String>,
    pub(in crate::cmd::webui) filter_type: Option<String>,
    pub(in crate::cmd::webui) filter_search: String,
    /// Current page number (1-indexed).
    pub(in crate::cmd::webui) page: usize,
    pub(in crate::cmd::webui) total_pages: usize,
    pub(in crate::cmd::webui) pagination_pages: Vec<usize>,
    /// Whether any non-default params are set for this section (used for
    /// details open heuristic).
    pub(in crate::cmd::webui) has_user_params: bool,
}

impl SectionState {
    /// Build a query-string fragment for this section's current filters
    /// (excludes sort, order, page).
    fn section_filter_qs(&self) -> String {
        let prefix = &self.sort_ctx.prefix;
        let mut parts = Vec::new();
        if let Some(ref p) = self.filter_priority {
            parts.push(format!("{prefix}priority={p}"));
        }
        if let Some(ref e) = self.filter_effort {
            parts.push(format!("{prefix}effort={e}"));
        }
        if let Some(ref l) = self.filter_label {
            parts.push(format!("{prefix}label={l}"));
        }
        if let Some(ref tt) = self.filter_type {
            parts.push(format!("{prefix}type={tt}"));
        }
        if !self.filter_search.is_empty() {
            parts.push(format!("{prefix}q={}", self.filter_search));
        }
        parts.join("&")
    }

    /// Build a pagination link for this section, preserving all page state.
    fn pagination_href(&self, target_page: &usize) -> String {
        let target_page = *target_page;
        let prefix = &self.sort_ctx.prefix;
        let mut qs = self.sort_ctx.preserve_qs.clone();
        // Add this section's filter params.
        let fqs = self.section_filter_qs();
        if !fqs.is_empty() {
            if !qs.is_empty() {
                qs.push('&');
            }
            qs.push_str(&fqs);
        }
        // Add sort/order for this section.
        if !qs.is_empty() {
            qs.push('&');
        }
        qs.push_str(&format!(
            "{prefix}sort={}&{prefix}order={}",
            self.sort_ctx.field, self.sort_ctx.order
        ));
        qs.push_str(&format!("&{prefix}page={target_page}"));
        format!("{}?{qs}", self.sort_ctx.base_href)
    }
}

#[derive(Template)]
#[template(path = "project.html")]
pub(super) struct ProjectTemplate {
    pub(super) all_projects: Vec<String>,
    pub(super) active_project: Option<String>,
    pub(super) project_name: String,
    pub(super) stats_open: usize,
    pub(super) stats_in_progress: usize,
    pub(super) stats_closed: usize,
    pub(super) next_up: Vec<ScoredEntry>,
    /// Current scoring mode: "impact" or "effort".
    pub(super) next_mode: String,
    pub(super) all_labels: Vec<String>,
    pub(super) in_progress: SectionState,
    pub(super) open: SectionState,
    pub(super) closed: SectionState,
}

impl ProjectTemplate {
    /// Build the complete current-page query string (all sections) for use
    /// in mutation redirect hidden fields.
    fn full_current_qs(&self) -> String {
        let mut parts = Vec::new();
        if self.next_mode == "effort" {
            parts.push("next_mode=effort".to_string());
        }
        for section in [&self.in_progress, &self.open, &self.closed] {
            let prefix = &section.sort_ctx.prefix;
            let fqs = section.section_filter_qs();
            if !fqs.is_empty() {
                parts.push(fqs);
            }
            if section.sort_ctx.field != "priority" || section.sort_ctx.order != "asc" {
                parts.push(format!(
                    "{prefix}sort={}&{prefix}order={}",
                    section.sort_ctx.field, section.sort_ctx.order
                ));
            }
            if section.page > 1 {
                parts.push(format!("{prefix}page={}", section.page));
            }
        }
        parts.join("&")
    }

    /// Build the current query string without next_mode, for use as hidden
    /// fields in the mode toggle form.
    fn section_only_qs(&self) -> String {
        let mut parts = Vec::new();
        for section in [&self.in_progress, &self.open, &self.closed] {
            let prefix = &section.sort_ctx.prefix;
            let fqs = section.section_filter_qs();
            if !fqs.is_empty() {
                parts.push(fqs);
            }
            if section.sort_ctx.field != "priority" || section.sort_ctx.order != "asc" {
                parts.push(format!(
                    "{prefix}sort={}&{prefix}order={}",
                    section.sort_ctx.field, section.sort_ctx.order
                ));
            }
            if section.page > 1 {
                parts.push(format!("{prefix}page={}", section.page));
            }
        }
        parts.join("&")
    }

    /// Build the full redirect URL for mutation forms, preserving current
    /// page state.
    fn mutation_redirect(&self) -> String {
        let qs = self.full_current_qs();
        if qs.is_empty() {
            format!("/projects/{}", self.project_name)
        } else {
            format!("/projects/{}?{qs}", self.project_name)
        }
    }
}
