use std::error::Error;
use std::fmt::{Debug, Formatter, Write};
use std::io::Cursor;
use std::ops::RangeInclusive;
use std::rc::Rc;

use antlr_rust::parser_rule_context::ParserRuleContext;
use ariadne::{ColorGenerator, Label, Report, ReportKind, Source};
use derive_more::Display;
use serde::Serialize;
use thiserror::Error;
use tsify_next::Tsify;

use crate::antlr::{fit, interval};
use crate::sql::expression::identifier::Identifier as SqlIdentifier;
use crate::types::Type;

/// Trait for contexts that can accumulate errors in a diagnostic sink
///
/// This trait is implemented by both `ParseContext` (for parse-phase errors)
/// and `StatementTranslationContext` (for type-check-phase errors), allowing
/// the `ErrorBuilder` to work generically with either context.
pub trait DiagnosticSink {
    /// Add an error to the diagnostic sink
    fn add_error(&mut self, error: Rc<TranslationError>);
}

#[derive(Debug, Clone, Default, Display, Serialize, PartialEq, Eq, Hash, Tsify)]
#[tsify(into_wasm_abi)]
pub enum Level {
    #[default]
    Error,
    Warning,
    Info,
}

#[derive(Debug, Default, Display, Clone, Copy, Eq, PartialEq, Hash, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum LanguageArea {
    #[default]
    #[display("finding matching function definitions")]
    FunctionCall,
    #[display("finding matching operator definitions")]
    Operator,
    #[display("performing dereference (the dot operator)")]
    Deref,
    #[display("performing index access (the square brackets)")]
    IndexAccess,
    #[display("parsing")]
    Parsing,
}

#[derive(Debug, Default, Display, Clone, Copy, Eq, PartialEq, Hash, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum Stage {
    #[default]
    #[display("translation")]
    Translation,
    #[display("parsing")]
    Parsing,
    #[display("semantic analysis")]
    SemanticAnalysis,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
#[error("{}@{:?}: {}\n{}", level, primary.interval, primary.message, source_desc.clone().unwrap_or_default())]
pub struct TranslationError {
    pub area: Option<LanguageArea>,
    pub stage: Stage,
    pub level: Level,
    pub primary: Context,
    pub supporting: Option<Vec<Context>>,
    pub source_desc: Option<String>,
}

#[derive(Debug, thiserror::Error, PartialEq, Eq, Serialize, Tsify, Clone)]
#[tsify(into_wasm_abi)]
#[error("{pretty}")]
pub struct ContextualTranslationError {
    pub error: TranslationError,
    pub pretty: String,
}

impl ContextualTranslationError {
    pub fn new(error: TranslationError, pretty: String) -> Self {
        Self { error, pretty }
    }
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash, Tsify)]
#[tsify(into_wasm_abi)]
pub struct Context {
    pub interval: RangeInclusive<usize>,
    pub message: String,
}

#[derive(Debug, Clone, thiserror::Error, Default, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct TranslationErrors(pub Vec<TranslationError>);

#[derive(Debug, thiserror::Error, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct ContextualTranslationErrors {
    pub hamelin: String,
    pub errors: Vec<ContextualTranslationError>,
}

impl Display for ContextualTranslationErrors {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        for e in &self.errors {
            writeln!(f, "{}", e)?;
        }

        Ok(())
    }
}

impl ContextualTranslationErrors {
    pub fn new(hamelin: String, errors: TranslationErrors) -> Self {
        let prettified_errors = errors
            .into_iter()
            .map(|mut e| {
                e.primary.interval = fit(&e.primary.interval, &hamelin);
                if let Some(supporting) = &mut e.supporting {
                    for c in supporting.iter_mut() {
                        c.interval = fit(&c.interval, &hamelin);
                    }
                }

                let pretty = e.make_pretty(&hamelin).unwrap_or_default();
                ContextualTranslationError::new(e, pretty)
            })
            .collect();

        Self {
            hamelin,
            errors: prettified_errors,
        }
    }
}

impl Context {
    pub fn new(interval: RangeInclusive<usize>, message: &str) -> Self {
        Self {
            interval,
            message: message.to_string(),
        }
    }

    pub fn from<'a, T>(tree: &T, message: &str) -> Self
    where
        T: ParserRuleContext<'a>,
    {
        Self::new(interval(tree), message)
    }
}

impl TranslationError {
    pub fn new(ctx: Context) -> Self {
        Self {
            primary: ctx,
            supporting: None,
            source_desc: None,
            area: Default::default(),
            stage: Default::default(),
            level: Default::default(),
        }
    }

    pub fn msg<'a, T>(tree: &T, message: &str) -> Self
    where
        T: ParserRuleContext<'a>,
    {
        Self {
            primary: Context::from(tree, message),
            area: Default::default(),
            level: Default::default(),
            stage: Default::default(),
            supporting: None,
            source_desc: None,
        }
    }

    pub fn wrap<'a, T, E>(tree: &T, source: E) -> Self
    where
        E: Error + Send + Sync + 'static,
        T: ParserRuleContext<'a>,
    {
        Self {
            primary: Context::from(tree, "error while translating"),
            source_desc: Some(source.to_string()),
            area: Default::default(),
            stage: Default::default(),
            level: Default::default(),
            supporting: None,
        }
    }

    pub fn wrap_box<'a, T>(tree: &T, source: Box<dyn Error + Send + Sync + 'static>) -> Self
    where
        T: ParserRuleContext<'a>,
    {
        Self {
            primary: Context::from(tree, "error while translating"),
            source_desc: Some(source.to_string()),
            area: Default::default(),
            stage: Default::default(),
            level: Default::default(),
            supporting: None,
        }
    }

    pub fn fatal(hamelin: &str, e: Box<dyn Error + Send + Sync + 'static>) -> Self {
        Self::new(Context::new(0..=hamelin.len() - 1, "fatal error")).with_source_boxed(e)
    }

    pub fn with_level(mut self, level: Level) -> Self {
        self.level = level;
        self
    }

    pub fn with_area(mut self, area: LanguageArea) -> Self {
        self.area = Some(area);
        self
    }

    pub fn with_stage(mut self, stage: Stage) -> Self {
        self.stage = stage;
        self
    }

    pub fn add_context(&mut self, span: RangeInclusive<usize>, message: &str) {
        match self.supporting {
            Some(ref mut ctx) => ctx.push(Context::new(span, message)),
            None => self.supporting = Some(vec![Context::new(span, message)]),
        }
    }

    pub fn with_context<'a, T>(mut self, tree: &T, message: &str) -> Self
    where
        T: ParserRuleContext<'a>,
    {
        self.add_context(interval(tree), message);
        self
    }

    pub fn with_context_vec(mut self, context: Vec<Context>) -> Self {
        self.supporting = Some(context);
        self
    }

    pub fn with_source<E>(mut self, source: E) -> Self
    where
        E: Error + Send + Sync + 'static,
    {
        self.source_desc = Some(source.to_string());
        self
    }

    pub fn with_source_boxed(mut self, source: Box<dyn Error + Send + Sync + 'static>) -> Self {
        self.source_desc = Some(source.to_string());
        self
    }

    /// A more explicit Into<TranslationErrors> for when inference can't make that work.
    pub fn single(self) -> TranslationErrors {
        TranslationErrors(vec![self])
    }

    pub fn single_result<R>(self) -> Result<R, TranslationErrors> {
        Err(self.single())
    }

    pub fn make_pretty(&self, hamelin: &String) -> Result<String, std::fmt::Error> {
        let input = hamelin.as_str();
        let source = Source::from(input);
        let mut string_buf = String::new();

        let mut colors = ColorGenerator::default();
        let kind = match self.level {
            Level::Error => ReportKind::Error,
            Level::Warning => ReportKind::Warning,
            Level::Info => ReportKind::Advice,
        };
        let mut report = Report::build(kind, self.primary.interval.clone())
            .with_message(format!(
                "problem doing {} {}",
                self.stage,
                self.area.map(|a| format!("in {}", a)).unwrap_or_default()
            ))
            .with_label(
                Label::new(self.primary.interval.clone())
                    .with_message(&self.primary.message)
                    .with_color(colors.next()),
            );

        if let Some(contexts) = &self.supporting {
            for c in contexts {
                report = report.with_label(
                    Label::new(c.interval.clone())
                        .with_message(c.message.clone())
                        .with_order(1)
                        .with_color(colors.next()),
                );
            }
        }

        if let Some(source) = &self.source_desc {
            report = report.with_note(format!("{}", source));
        }

        let final_report = report.finish();
        let buf = Vec::new();
        let mut cursor = Cursor::new(buf);
        final_report
            .write(source.clone(), &mut cursor)
            .map_err(|_| std::fmt::Error)?;
        write!(string_buf, "{}", String::from_utf8_lossy(cursor.get_ref()))?;

        Ok(string_buf)
    }
}

impl TranslationErrors {
    pub fn add(&mut self, error: TranslationError) {
        self.0.push(error);
    }

    /// Alias for add_all to match standard library naming
    pub fn extend(&mut self, mut errors: TranslationErrors) {
        self.0.append(&mut errors.0);
    }

    pub fn with_all(mut self, other: Self) -> Self {
        self.extend(other);
        self
    }

    pub fn or_ok<R>(self, result: R) -> Result<R, TranslationErrors> {
        if self.is_empty() {
            Ok(result)
        } else {
            Err(self)
        }
    }

    pub fn expect<'a, O, T>(tree: &T, opt: Option<O>) -> Result<O, Self>
    where
        T: ParserRuleContext<'a>,
    {
        opt.ok_or_else(|| {
            TranslationError::new(Context::from(
                tree,
                "expected value not present during parse of clause",
            ))
            .single()
        })
    }

    pub fn consume_errors<R>(&mut self, result: Result<R, TranslationErrors>) -> Option<R> {
        match result {
            Ok(r) => Some(r),
            Err(e) => {
                self.extend(e);
                None
            }
        }
    }

    pub fn from_vec<R>(
        results: Vec<Result<R, TranslationErrors>>,
    ) -> Result<Vec<R>, TranslationErrors> {
        let mut errors = vec![];
        let mut successes = vec![];
        for r in results {
            match r {
                Ok(r) => successes.push(r),
                Err(e) => errors.push(e),
            }
        }

        if !errors.is_empty() {
            Err(errors.into())
        } else {
            Ok(successes)
        }
    }

    pub fn from_2<R1, R2>(
        result1: Result<R1, TranslationErrors>,
        result2: Result<R2, TranslationErrors>,
    ) -> Result<(R1, R2), TranslationErrors> {
        match (result1, result2) {
            (Ok(r1), Ok(r2)) => Ok((r1, r2)),
            (r1, r2) => {
                let mut errors = Self::default();
                if let Err(e) = r1 {
                    errors.extend(e);
                }
                if let Err(e) = r2 {
                    errors.extend(e);
                }
                Err(errors)
            }
        }
    }

    pub fn from_2_option<R1, R2>(
        err: TranslationError,
        result1: Option<R1>,
        result2: Option<R2>,
    ) -> Result<(R1, R2), TranslationErrors> {
        match (result1, result2) {
            (Some(r1), Some(r2)) => Ok((r1, r2)),
            _ => Err(err.single()),
        }
    }

    pub fn from_3<R1, R2, R3>(
        result1: Result<R1, TranslationErrors>,
        result2: Result<R2, TranslationErrors>,
        result3: Result<R3, TranslationErrors>,
    ) -> Result<(R1, R2, R3), TranslationErrors> {
        match (result1, result2, result3) {
            (Ok(r1), Ok(r2), Ok(r3)) => Ok((r1, r2, r3)),
            (r1, r2, r3) => {
                let mut errors = Self::default();
                if let Err(e) = r1 {
                    errors.extend(e);
                }
                if let Err(e) = r2 {
                    errors.extend(e);
                }
                if let Err(e) = r3 {
                    errors.extend(e);
                }
                Err(errors)
            }
        }
    }

    pub fn maybe_with_area(mut self, area: LanguageArea) -> Self {
        for error in self.0.iter_mut() {
            if error.area.is_none() {
                error.area = Some(area.clone());
            }
        }
        self
    }

    pub fn maybe_with_stage(mut self, stage: Stage) -> Self {
        for error in self.0.iter_mut() {
            if error.stage == Stage::Translation {
                error.stage = stage.clone();
            }
        }
        self
    }

    pub fn iter(&self) -> std::slice::Iter<'_, TranslationError> {
        self.0.iter()
    }

    pub fn into_iter(self) -> std::vec::IntoIter<TranslationError> {
        self.0.into_iter()
    }

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

    pub fn len(&self) -> usize {
        self.0.len()
    }
}

impl From<TranslationError> for TranslationErrors {
    fn from(error: TranslationError) -> Self {
        Self(vec![error])
    }
}

impl Display for TranslationErrors {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        for error in &self.0 {
            write!(f, "{}\n\n", error)?;
        }
        Ok(())
    }
}

impl From<Vec<TranslationErrors>> for TranslationErrors {
    fn from(errors: Vec<TranslationErrors>) -> Self {
        let mut ret = Self::default();
        for mut error in errors.into_iter() {
            ret.0.append(&mut error.0);
        }
        ret
    }
}

#[derive(thiserror::Error, Debug)]
pub struct UnexpectedType {
    pub expr_type: Type,
    pub expected: Vec<Type>,
}

impl UnexpectedType {
    pub fn new(typ: Type, expected: Vec<Type>) -> Self {
        Self {
            expr_type: typ,
            expected,
        }
    }
}

impl Display for UnexpectedType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "unexpected type: {}\n", self.expr_type)?;

        if self.expected.len() == 1 {
            writeln!(f, "expected: {}\n", self.expected[0])?;
        } else {
            writeln!(f, "expected one of:\n")?;
            for exp in &self.expected {
                write!(f, "  {}\n", exp)?;
            }
        }
        Ok(())
    }
}

#[derive(Error, Debug)]
#[error("Invalid cast attempt from {from} to {to}")]
pub struct InvalidCast {
    from: Type,
    to: Type,
}

impl InvalidCast {
    pub fn new(from: Type, to: Type) -> Self {
        Self { from, to }
    }
}

#[derive(thiserror::Error, Debug, Eq, PartialEq)]
pub struct NonMergeableTypes {
    pub base: Type,
    pub other: Type,
    pub at: Option<SqlIdentifier>,
}

impl NonMergeableTypes {
    pub fn new(base: Type, other: Type) -> Self {
        Self {
            base,
            other,
            at: None,
        }
    }

    pub fn prepend_at(mut self, at: SqlIdentifier) -> Self {
        self.at = match self.at {
            None => Some(at.into()),
            Some(i) => Some(i.prepend(at).into()),
        };
        self
    }
}

impl Display for NonMergeableTypes {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.at {
            Some(at) => write!(
                f,
                "The types found at {} are not mergeable: {}, {}",
                at.to_hamelin(),
                self.base,
                self.other
            ),
            None => write!(
                f,
                "The types are not mergeable: {}, {}",
                self.base, self.other
            ),
        }
    }
}
