pub mod config;
pub mod construction;

pub use fmt::python_repr;
mod eq;
pub mod fix;
mod fmt;
pub mod path;
mod raw_string;
pub(crate) use raw_string::RawString;

use std::{
    borrow::Cow,
    fmt::Write,
    sync::{Arc, Weak},
};

use fix::SourceFix;
use hashbrown::HashSet;
use path::PathStep;
use uuid::Uuid;

use crate::{
    marker::PositionMarker,
    regex::{RegexMode, RegexModeGroup},
};

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum CaseFold {
    #[default]
    None,
    Upper,
    Lower,
}

#[derive(Debug, Clone, PartialEq)]
pub enum TupleSerialisedSegment {
    Str(String, String),
    Nested(String, Vec<TupleSerialisedSegment>),
}

#[derive(Debug, Clone)]
pub struct Token {
    pub token_type: Cow<'static, str>,
    pub class_name: Cow<'static, str>,
    pub instance_types: Vec<String>,
    pub class_types: Arc<HashSet<String>>,
    pub comment_separate: bool,
    pub is_meta: bool,
    pub allow_empty: bool,
    pub pos_marker: Option<PositionMarker>,
    raw: RawString,
    is_whitespace: bool,
    is_code: bool,
    is_comment: bool,
    _default_raw: Cow<'static, str>,
    pub indent_value: i32,
    pub is_templated: bool,
    pub block_uuid: Option<Uuid>,
    pub source_str: Option<String>,
    pub block_type: Option<String>,
    parent: Option<Weak<Token>>,
    parent_idx: Option<usize>,
    pub segments: Vec<Token>,
    preface_modifier: Cow<'static, str>,
    suffix: Cow<'static, str>,
    pub uuid: u128,
    pub source_fixes: Option<Vec<SourceFix>>,
    pub trim_start: Option<Vec<String>>,
    pub trim_chars: Option<Vec<String>>,
    /// Pre-computed index of matching bracket for O(1) lookup during parsing.
    /// For opening brackets like '(', '[', '{', this points to the matching closing bracket.
    /// For closing brackets like ')', ']', '}', this points back to the matching opening bracket.
    /// None for non-bracket tokens or unmatched brackets.
    pub matching_bracket_idx: Option<usize>,
}

impl Token {
    fn comments(&self) -> Vec<Token> {
        self.segments
            .clone()
            .into_iter()
            .filter(|s| s.is_type(&["comment"]))
            .collect::<Vec<_>>()
    }

    fn non_comments(&self) -> Vec<Token> {
        self.segments
            .clone()
            .into_iter()
            .filter(|s| !s.is_type(&["comment"]))
            .collect::<Vec<_>>()
    }

    /// Returns True if this segment is code.
    pub fn is_code(&self) -> bool {
        match self.is_raw() {
            true => self.is_code,
            false => self.segments.iter().any(|s| s.is_code()),
        }
    }

    fn code_indices(&self) -> Vec<usize> {
        self.segments
            .iter()
            .enumerate()
            .filter(|(_i, s)| s.is_code())
            .map(|(i, _s)| i)
            .collect()
    }

    pub fn is_comment(&self) -> bool {
        match self.is_raw() {
            true => self.is_comment,
            false => self.segments.iter().all(|s| s.is_comment()),
        }
    }

    pub fn is_whitespace(&self) -> bool {
        match self.is_raw() {
            true => self.is_whitespace,
            false => self.segments.iter().all(|s| s.is_whitespace()),
        }
    }

    pub fn raw(&self) -> &str {
        self.raw.as_str()
    }

    pub fn raw_upper(&self) -> &str {
        self.raw.upper()
    }

    /// Get the quoted_value pattern for this token (if any)
    pub fn quoted_value(&self) -> Option<&(String, RegexModeGroup)> {
        self.raw.quoted_value()
    }

    /// Get the escape_replacements patterns for this token (if any)
    pub fn escape_replacements(&self) -> Option<&Vec<(String, String)>> {
        self.raw.escape_replacements()
    }

    /// Cheaply-cloneable (`Arc`) handle to the escape_replacements patterns,
    /// for carrying them into a segment/match without deep-copying the pairs.
    pub fn escape_replacements_arc(&self) -> Option<std::sync::Arc<Vec<(String, String)>>> {
        self.raw.escape_replacements_arc()
    }

    /// Get the casefold mode for this token (`CaseFold::None` if unset)
    pub fn casefold(&self) -> CaseFold {
        self.raw.casefold()
    }

    pub fn normalize(
        value: &str,
        quoted_value: Option<&(String, RegexModeGroup)>,
        escape_replacements: Option<&Vec<(String, String)>>,
    ) -> String {
        let mut str_buffer = value.to_string();

        if let Some((regex_str, idx)) = quoted_value {
            if let Some(captured) = RegexMode::cached(regex_str).capture(idx.clone(), value) {
                str_buffer = captured
            }
        }

        // PYTHON PARITY: RawSegment._get_normalized_value (segments/raw.py)
        // applies every escape_replacements pair in order, each on the result
        // of the previous - not just the first pair.
        if let Some(replacements) = escape_replacements {
            for (regex_str, replacement) in replacements {
                str_buffer =
                    RegexMode::cached(regex_str).replace_all(&str_buffer, replacement.as_str());
            }
        }

        str_buffer
    }

    pub fn raw_segments(&self) -> Vec<Token> {
        match self.is_raw() {
            true => vec![self.clone()],
            false => self
                .segments
                .iter()
                .flat_map(|s| s.raw_segments())
                .collect::<Vec<_>>(),
        }
    }

    /// The set of full types for this token, including inherited.
    /// Adds the surrogate type for raw segments.
    pub fn class_types(&self) -> HashSet<String> {
        let mut full_types = self.instance_types.iter().cloned().collect::<HashSet<_>>();
        full_types.extend(self.class_types.iter().cloned());
        full_types
    }

    pub fn descendant_type_set(&self) -> HashSet<String> {
        self.segments
            .iter()
            .flat_map(|seg| {
                seg.descendant_type_set()
                    .union(&seg.class_types())
                    .cloned()
                    .collect::<HashSet<String>>()
            })
            .collect::<HashSet<String>>()
    }

    pub fn direct_descendant_type_set(&self) -> HashSet<String> {
        self.segments
            .iter()
            .flat_map(|seg| seg.class_types())
            .collect::<HashSet<String>>()
    }

    pub fn raw_segments_with_ancestors(&self) -> Vec<(Token, Vec<PathStep>)> {
        todo!()
    }

    pub fn source_fixes(&self) -> Vec<SourceFix> {
        match self.is_raw() {
            true => self.source_fixes.clone().unwrap_or_default(),
            false => self
                .segments
                .iter()
                .flat_map(|s| s.source_fixes())
                .collect(),
        }
    }

    pub fn first_non_whitespace_segment_raw_upper(&self) -> Option<String> {
        self.raw_segments().iter().find_map(|seg| {
            let upper = seg.raw_upper();
            if !upper.trim().is_empty() {
                Some(upper.to_owned())
            } else {
                None
            }
        })
    }

    pub fn is_templated(&self) -> bool {
        let pos_marker = self.pos_marker.clone().expect("PositionMarker must be set");
        pos_marker.source_slice.start != pos_marker.source_slice.stop && !pos_marker.is_literal()
    }

    pub fn get_type(&self) -> String {
        self.token_type.to_string()
    }

    /// Get all types for this token (instance_types + class_types)
    /// This is equivalent to Python's class_types property
    pub fn get_all_types(&self) -> hashbrown::HashSet<String> {
        let mut types = hashbrown::HashSet::new();
        types.extend(self.instance_types.iter().cloned());
        types.extend(self.class_types.iter().cloned());
        types
    }

    pub fn preface_modifier(&self) -> String {
        self.preface_modifier.to_string()
    }

    pub fn is_type(&self, seg_types: &[&str]) -> bool {
        if self
            .instance_types
            .iter()
            .any(|s| seg_types.contains(&s.as_str()))
        {
            return true;
        }
        self.class_is_type(seg_types)
    }

    pub fn get_raw_segments(&self) -> Vec<Token> {
        todo!()
    }

    pub fn raw_trimmed(&self) -> String {
        let mut raw_buff = self.raw.as_str().to_owned();

        // Trim start sequences
        if let Some(trim_start) = &self.trim_start {
            for seq in trim_start {
                raw_buff = raw_buff.strip_prefix(seq).unwrap_or(&raw_buff).to_string();
            }
        }

        // Trim specified characters from both ends
        if let Some(trim_chars) = &self.trim_chars {
            raw_buff = self.raw.as_str().to_owned(); // Reset raw_buff before trimming chars

            for seq in trim_chars {
                while raw_buff.starts_with(seq) {
                    raw_buff = raw_buff.strip_prefix(seq).unwrap_or(&raw_buff).to_string();
                }
                while raw_buff.ends_with(seq) {
                    raw_buff = raw_buff.strip_suffix(seq).unwrap_or(&raw_buff).to_string();
                }
            }
        }

        raw_buff
    }

    /// The normalized form of this token's raw value: the quoted value extracted
    /// and escape sequences replaced, per this token's dialect config.
    ///
    /// Computed lazily and cached, so tokens never inspected by a rule pay
    /// nothing, and the common case (no transform spec) avoids all regex work.
    pub fn raw_normalized(&self) -> String {
        self.raw.normalized().to_owned()
    }

    pub fn stringify(&self, ident: usize, tabsize: usize, code_only: bool) -> String {
        let mut buff = String::new();
        let preface = self.preface(ident, tabsize);
        writeln!(buff, "{}", preface).unwrap();

        if !code_only && self.comment_separate && !self.comments().is_empty() {
            if !self.comments().is_empty() {
                writeln!(buff, "{}Comments:", " ".repeat((ident + 1) * tabsize)).unwrap();
                for seg in &self.comments() {
                    let segment_string = seg.stringify(ident + 2, tabsize, code_only);
                    buff.push_str(&segment_string);
                }
            }

            if !self.non_comments().is_empty() {
                writeln!(buff, "{}Code:", " ".repeat((ident + 1) * tabsize)).unwrap();
                for seg in &self.non_comments() {
                    let segment_string = seg.stringify(ident + 2, tabsize, code_only);
                    buff.push_str(&segment_string);
                }
            }
        } else {
            for seg in &self.segments {
                if !code_only || seg.is_code {
                    let segment_string = seg.stringify(ident + 1, tabsize, code_only);
                    buff.push_str(&segment_string);
                }
            }
        }

        buff
    }

    pub fn edit(&self, raw: Option<String>, source_fixes: Option<Vec<SourceFix>>) -> Self {
        let new_raw = raw.unwrap_or_else(|| self.raw.as_str().to_owned());
        Self {
            // Carry over the transform spec so the edited raw normalizes the same way.
            raw: RawString::new(
                new_raw,
                self.raw.quoted_value().cloned(),
                self.raw.escape_replacements_arc(),
                self.raw.casefold(),
            ),
            source_fixes: Some(source_fixes.unwrap_or(self.source_fixes())),
            uuid: crate::identity::next_id(),
            ..self.clone()
        }
    }

    // pub fn _get_raw_segment_kwargs(&self) -> HashMap<String, _> {
    //     let kwargs = HashMap::new();
    //     kwargs.insert("quoted_value", self.quoted_value);
    //     kwargs.insert("escape_replacements", vec![self.escape_replacement]);
    //     kwargs
    // }

    pub fn iter_unparseables(&self) -> Vec<Token> {
        self.segments
            .iter()
            .flat_map(|s| s.iter_unparseables())
            .collect()
    }

    pub fn set_parent(&mut self, parent: Arc<Token>, idx: usize) {
        self.parent = Some(Arc::downgrade(&parent));
        self.parent_idx = Some(idx);
    }

    pub fn class_is_type(&self, seg_types: &[&str]) -> bool {
        let seg_hash: HashSet<&str> = seg_types.iter().cloned().collect();
        !self
            .class_types
            .iter()
            .filter(|s| seg_hash.contains(s.as_str()))
            .collect::<Vec<_>>()
            .is_empty()
    }

    pub fn count_segments(&self, raw_only: bool) -> usize {
        if self.is_raw() {
            1
        } else {
            let self_count = if raw_only { 0 } else { 1 };
            self.segments
                .iter()
                .fold(0, |acc, s| acc + s.count_segments(raw_only) + self_count)
        }
    }

    pub fn is_raw(&self) -> bool {
        self.segments.is_empty()
    }

    pub fn block_type(&self) -> Option<String> {
        self.block_type.clone()
    }

    pub fn recursive_crawl(
        &self,
        seg_types: &[&str],
        recurse_into: bool,
        no_recursive_seg_type: Option<&[&str]>,
        allow_self: bool,
    ) -> Vec<Token> {
        let mut results = Vec::new();

        // If recurse_into is False and this matches, don't recurse
        if !recurse_into && self.is_type(seg_types) {
            if allow_self {
                results.push(self.clone());
            }
            return results;
        }

        // Check if self matches the given segment types
        if allow_self && self.is_type(seg_types) {
            results.push(self.clone());
        }

        // Convert no_recursive_seg_type to HashSet for efficient lookups
        let no_recursive_set: HashSet<&str> = no_recursive_seg_type
            .unwrap_or(&[])
            .iter()
            .cloned()
            .collect();

        // Recursively process child segments
        for seg in &self.segments {
            if no_recursive_set.contains(seg.token_type.as_ref()) {
                continue;
            }
            results.extend(seg.recursive_crawl(
                seg_types,
                recurse_into,
                no_recursive_seg_type,
                true,
            ));
        }

        results
    }

    pub fn path_to(self, other: Self) -> Vec<PathStep> {
        // Return empty if they are the same segment.
        if self == other {
            return vec![];
        }

        // If there are no child segments, return empty.
        if self.segments.is_empty() {
            return vec![];
        }

        // Identifying the highest parent we can using any preset parent values.
        let mut midpoint = other.clone();
        let mut lower_path = Vec::new();

        while let Some(weak_parent) = &midpoint.parent.clone().as_ref() {
            if let Some(parent) = weak_parent.upgrade() {
                let parent_idx = midpoint.parent_idx.expect("Parent index must be set.");

                lower_path.push(PathStep {
                    segment: Arc::clone(&parent),
                    idx: parent_idx,
                    len: parent.segments.len(),
                    code_idxs: parent.code_indices().clone(),
                });

                midpoint = Arc::unwrap_or_clone(parent);
                if midpoint == self {
                    break;
                }
            } else {
                break;
            }
        }

        // Reverse the path so far
        lower_path.reverse();

        // If we have already found the parent, return.
        if midpoint == self {
            return lower_path;
        }
        // If we've gone all the way up to the file segment, return empty.
        if midpoint.class_is_type(&["file"]) {
            return vec![];
        }
        // Check if midpoint is within self's range.
        if !(self.get_start_loc() <= midpoint.get_start_loc()
            && midpoint.get_start_loc() <= self.get_end_loc())
        {
            return vec![];
        }

        // Now, work downward from `self` toward `midpoint`.
        for (idx, seg) in self.segments.clone().iter().enumerate() {
            // Set the parent if it's not already set.
            let seg = seg.clone();
            seg.clone().set_parent(Arc::new(self.clone()), idx);

            let step = PathStep {
                segment: Arc::new(self.clone()),
                idx,
                len: self.segments.clone().len(),
                code_idxs: self.code_indices().clone(),
            };

            // If we found the target
            if seg == midpoint {
                let mut result = vec![step];
                result.extend(lower_path);
                return result;
            }

            // Check recursively if a path exists
            let res = seg.path_to(midpoint.clone());
            if !res.is_empty() {
                let mut result = vec![step];
                result.extend(res);
                result.extend(lower_path);
                return result;
            }
        }

        // Not found.
        vec![]
    }

    pub fn get_start_loc(&self) -> (usize, usize) {
        self.pos_marker
            .clone()
            .expect("PositionMarker unset")
            .working_loc()
    }

    pub fn get_end_loc(&self) -> (usize, usize) {
        self.pos_marker
            .clone()
            .expect("PositionMarker unset")
            .working_loc_after(self.raw.as_str())
    }

    pub fn recursive_crawl_all(&self, reverse: bool) -> Box<dyn Iterator<Item = &Token> + '_> {
        if reverse {
            Box::new(
                self.segments
                    .iter()
                    .rev()
                    .flat_map(move |seg| seg.recursive_crawl_all(reverse))
                    .chain(std::iter::once(self)),
            )
        } else {
            Box::new(
                std::iter::once(self).chain(
                    self.segments
                        .iter()
                        .flat_map(move |seg| seg.recursive_crawl_all(reverse)),
                ),
            )
        }
    }

    fn preface(&self, ident: usize, tabsize: usize) -> String {
        let padding = " ".repeat(ident * tabsize);
        let padded_type = format!("{}{}{}:", padding, self.preface_modifier, self.get_type());

        let pos = self.pos_marker.clone();
        let suffix = self.suffix.clone();

        let preface = format!(
            "{:<20}|{:<60}  {}",
            pos.clone()
                .expect("PositionMarker unset")
                .to_source_string(),
            padded_type,
            suffix
        );

        preface.trim_end().to_string()
    }

    pub fn to_tuple(
        &self,
        code_only: Option<bool>,
        show_raw: Option<bool>,
        include_meta: Option<bool>,
    ) -> TupleSerialisedSegment {
        let code_only = code_only.unwrap_or_default();
        let show_raw = show_raw.unwrap_or_default();
        let include_meta = include_meta.unwrap_or_default();
        // If `show_raw` is true and there are no child segments, return (type, raw)
        if show_raw && self.segments.is_empty() {
            return TupleSerialisedSegment::Str(self.get_type(), self.raw.as_str().to_owned());
        }

        // Determine filtering criteria for child segments
        let filtered_segments: Vec<TupleSerialisedSegment> = self
            .segments
            .iter()
            .filter(|seg| {
                if code_only {
                    seg.is_code && !seg.is_meta
                } else {
                    include_meta || !seg.is_meta
                }
            })
            .map(|seg| seg.to_tuple(Some(code_only), Some(show_raw), Some(include_meta)))
            .collect();

        TupleSerialisedSegment::Nested(self.get_type(), filtered_segments)
    }

    pub fn copy(
        &self,
        segments: Option<Vec<Token>>,
        parent: Option<Arc<Token>>,
        parent_idx: Option<usize>,
    ) -> Token {
        let mut new_segment = self.clone();
        new_segment.parent = parent.as_ref().map(Arc::downgrade);
        new_segment.parent_idx = parent_idx;

        if let Some(ref segs) = segments {
            new_segment.segments = segs.clone();
        } else {
            new_segment.segments = self
                .segments
                .iter()
                .enumerate()
                .map(|(idx, seg)| seg.copy(None, Some(Arc::new(new_segment.clone())), Some(idx)))
                .collect();
        }

        new_segment
    }

    pub fn position_segments(segments: &[Token], parent_pos: PositionMarker) -> Vec<Token> {
        assert!(
            !segments.is_empty(),
            "position_segments called on empty sequence."
        );
        let mut line_no = parent_pos.working_line_no;
        let mut line_pos = parent_pos.working_line_pos;

        let mut segment_buffer = Vec::new();

        for (idx, segment) in segments.iter().enumerate() {
            let old_position = segment.pos_marker.clone();
            let mut new_position = segment.pos_marker.clone();

            // If position is missing, try to infer it
            if new_position.is_none() {
                let mut start_point = None;
                if idx > 0 {
                    let prev_seg: &Token = &segment_buffer[idx - 1];
                    if let Some(ref pos_marker) = prev_seg.pos_marker {
                        start_point = Some(pos_marker.end_point_marker());
                    }
                } else {
                    start_point = Some(parent_pos.start_point_marker());
                }

                // Search forward for the end point
                let mut end_point = None;
                for fwd_seg in &segments[idx + 1..] {
                    if let Some(ref pos_marker) = fwd_seg.pos_marker {
                        end_point = Some(pos_marker.start_point_marker());
                        break;
                    }
                }

                new_position = match (start_point, end_point) {
                    (Some(start), Some(end)) if start != end => {
                        Some(PositionMarker::from_points(&start, &end))
                    }
                    (Some(start), _) => Some(start),
                    (_, Some(end)) => Some(end),
                    _ => panic!("Unable to position new segment"),
                };
            }

            let new_position = new_position.expect("Position should be assigned");
            let new_position = new_position.with_working_position(line_no, line_pos);
            let (new_line_no, new_line_pos) =
                new_position.infer_next_position(segment.raw.as_str(), line_no, line_pos);
            line_no = new_line_no;
            line_pos = new_line_pos;

            // If position changed, recursively process child segments before copying
            let new_segment =
                if !segment.segments.is_empty() && old_position != Some(new_position.clone()) {
                    let child_segments =
                        Token::position_segments(&segment.segments, new_position.clone());
                    segment.copy(Some(child_segments), None, None)
                } else {
                    segment.copy(None, None, None)
                };

            segment_buffer.push(new_segment);
        }

        segment_buffer
    }

    // /// Simplifies the structure of the token recursively for serialization.
    // pub fn structural_simplify(&self) -> HashMap<String, Option<serde_json::Value>> {
    //     let mut result = HashMap::new();
    //     let key = self.get_type();

    //     if self.segments.is_empty() {
    //         // If there are no child segments, return the raw value.
    //         result.insert(key, Some(serde_json::Value::String(self.raw.clone())));
    //     } else {
    //         // Simplify all child segments recursively.
    //         let mut child_results = Vec::new();
    //         for segment in &self.segments {
    //             child_results.push(serde_json::Value::Object(
    //                 segment.structural_simplify(),
    //             ));
    //         }

    //         // Check for duplicate keys in child results.
    //         let mut subkeys = Vec::new();
    //         for child in &child_results {
    //             if let serde_json::Value::Object(map) = child {
    //                 subkeys.extend(map.keys().cloned());
    //             }
    //         }

    //         if subkeys.len() != subkeys.iter().collect::<HashSet<_>>().len() {
    //             // If there are duplicate keys, use a list of child objects.
    //             result.insert(key, Some(serde_json::Value::Array(child_results)));
    //         } else {
    //             // Otherwise, merge child objects into a single map.
    //             let mut merged_map = HashMap::new();
    //             for child in child_results {
    //                 if let serde_json::Value::Object(map) = child {
    //                     for (k, v) in map {
    //                         merged_map.insert(k, v);
    //                     }
    //                 }
    //             }
    //             result.insert(key, Some(serde_json::Value::Object(merged_map)));
    //         }
    //     }

    //     result
    // }
}
