use super::node::Span;
use crate::antlr::hamelinparser::{
    PipelineAltContextAttrs, PipelineContextAll, PipelineEOFContextAttrs,
};
use crate::antlr::interval;
use crate::err::TranslationErrors;
use crate::resilient_parse_pipeline;
use crate::tree::ast::context::{FromCst, ParseContext};
use crate::tree::ast::identifier::Identifier;
use crate::tree::ast::{
    IntoParsed, IntoTyped, ParseOptions, ParseWithContext, ParseWithErrors,
    QueryTranslationOptions, TypeCheck,
};
use crate::tree::typed_ast::context::StatementTranslationContext;
use crate::tree::typed_ast::pipeline::TypedPipeline;
use std::ops::RangeInclusive;
use std::rc::Rc;

use super::command::{Command, CommandKind, SelectCommand};
use super::expression::Expression;
use super::node::Spannable;

/// A pipeline is a sequence of commands that transform data
#[derive(Debug, Clone)]
pub struct Pipeline {
    pub span: Span,
    pub commands: Vec<Rc<Command>>,
}

impl ParseWithErrors for Pipeline {
    fn parse_with_errors(input: impl Into<String>) -> (Self, TranslationErrors) {
        Self::parse_with_context(input.into(), ParseContext::new())
    }
}

impl ParseWithContext for Pipeline {
    fn parse_with_context(input: String, mut ctx: ParseContext) -> (Self, TranslationErrors) {
        match resilient_parse_pipeline(input) {
            Ok((csteof, parse_errors)) => match csteof.pipeline() {
                Some(cst) => {
                    let ast = Self::from_cst_with_context(cst, &mut ctx);
                    let mut errors = ctx.take_errors();
                    errors.extend(parse_errors);
                    (ast, errors)
                }
                None => {
                    let errors = ctx.take_errors();
                    let mut all_errors = errors;
                    all_errors.extend(parse_errors);
                    (
                        Self {
                            span: Span::NONE,
                            commands: vec![],
                        },
                        all_errors,
                    )
                }
            },
            Err(e) => {
                let error = ctx
                    .error("fatal error initializing parser")
                    .with_source_boxed(e.into())
                    .emit();
                (
                    Self {
                        span: Span::NONE,
                        commands: vec![],
                    },
                    (*error).clone().single(),
                )
            }
        }
    }
}

impl IntoParsed for Pipeline {
    type Options = ParseOptions<Pipeline>;

    fn parse_with(input: impl Into<String>) -> ParseOptions<Pipeline> {
        ParseOptions::new(input)
    }
}

impl Spannable for Pipeline {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl Pipeline {
    /// Set the span for this pipeline
    pub fn at(mut self, span: Span) -> Self {
        self.span = span;
        self
    }
}

impl PartialEq for Pipeline {
    fn eq(&self, other: &Self) -> bool {
        // Compare only the semantic content (commands), ignoring CST
        self.commands == other.commands
    }
}

impl From<Expression> for Pipeline {
    fn from(expr: Expression) -> Self {
        use super::clause::Assignment;
        use super::identifier::SimpleIdentifier;

        // Infer identifier from expression, fall back to "_expr"
        let identifier = Identifier::infer_from_expression(&expr)
            .unwrap_or_else(|| SimpleIdentifier::new("_expr").into());

        let command = Command {
            span: Span::NONE,
            kind: CommandKind::Select(SelectCommand {
                clauses: vec![Rc::new(Assignment::from_parts(identifier, expr).into())],
            }),
        };

        Pipeline {
            span: Span::NONE,
            commands: vec![Rc::new(command)],
        }
    }
}

impl FromCst<Rc<PipelineContextAll<'static>>> for Pipeline {
    fn from_cst_with_context(cst: Rc<PipelineContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        // Convert each command context to a Command
        let commands: Vec<Rc<Command>> = match cst.as_ref() {
            PipelineContextAll::PipelineAltContext(pctx) => pctx
                .command_all()
                .into_iter()
                .map(|cmd_cst| Rc::new(Command::from_cst_with_context(cmd_cst.clone(), ctx)))
                .collect(),
            PipelineContextAll::Error(_) => vec![],
        };

        Pipeline {
            span: interval(cst.as_ref()).into(),
            commands,
        }
    }
}

impl TypeCheck for Pipeline {
    type Output = TypedPipeline;

    fn type_check_with_context(
        ast: Rc<Self>,
        ctx: &mut StatementTranslationContext,
    ) -> Self::Output {
        TypedPipeline::from_ast_with_context(ast, ctx)
    }
}

impl IntoTyped for Pipeline {
    type Output = TypedPipeline;
    type Options = QueryTranslationOptions<Pipeline>;

    fn typed_with(self) -> QueryTranslationOptions<Pipeline> {
        QueryTranslationOptions::from_ast(Rc::new(self))
    }
}

impl IntoTyped for Rc<Pipeline> {
    type Output = TypedPipeline;
    type Options = QueryTranslationOptions<Pipeline>;

    fn typed_with(self) -> QueryTranslationOptions<Pipeline> {
        QueryTranslationOptions::from_ast(self)
    }
}
