use crate::{
    cli::{
        parser::{
            CommonArgsOutputForceOverWrite, PgSchemaValidateArgs, ShaclValidateArgs, ShexValidateArgs, ValidateArgs,
        },
        wrappers::ValidationModeCli,
    },
    commands::{
        PgSchemaValidateCommand, ShaclValidateCommand, ShexValidateCommand,
        base::{Command, CommandContext},
    },
};
use anyhow::{Result, anyhow};

/// Implementation of the `validate` command.
///
/// This struct holds the specific arguments parsed by `clap` and
/// implements the [Command] trait to execute validation logic.
pub struct ValidateCommand {
    /// Arguments specific to validate.
    args: ValidateArgs,
}

impl ValidateCommand {
    pub fn new(args: ValidateArgs) -> Self {
        Self { args }
    }

    /// Convert ValidateArgs to ShexValidateArgs
    fn to_shex_args(&self) -> Result<ShexValidateArgs> {
        Ok(ShexValidateArgs {
            data: self.args.data.clone(),
            schema: self
                .args
                .schema
                .clone()
                .ok_or_else(|| anyhow!("schema is required for ShEx validation"))?,
            schema_format: self.args.schema_format,
            shapemap: self.args.shapemap.clone(),
            shapemap_format: Some(self.args.shapemap_format),
            node: self.args.node.clone(),
            sort_by: self.args.sort_by.into(),
            shape: self.args.shape.clone(),
            data_format: self.args.data_format,
            base_schema: self.args.base_schema.clone(),
            base_data: self.args.base_data.clone(),
            reader_mode: self.args.reader_mode,
            endpoint: self.args.endpoint.clone(),
            result_format: self.args.result_format.into(),
            map_state: self.args.map_state.clone(),
            common: self.args.common.clone(),
        })
    }

    /// Convert ValidateArgs to ShaclValidateArgs  
    fn to_shacl_args(&self) -> Result<ShaclValidateArgs> {
        Ok(ShaclValidateArgs {
            data: self.args.data.clone(),
            data_format: self.args.data_format,
            base_data: self.args.base_data.clone(),
            reader_mode: self.args.reader_mode,
            shapes: self.args.schema.clone(),
            shapes_format: self.args.schema_format.try_into()?,
            base_shapes: self.args.base_schema.clone(),
            endpoint: self.args.endpoint.clone(),
            mode: self.args.shacl_validation_mode,
            result_format: self.args.result_format.into(),
            sort_by: self.args.sort_by.into(),
            common: self.args.common.clone(),
        })
    }

    /// Convert ValidateArgs to PgSchemaValidateArgs
    fn to_pgschema_args(&self) -> Result<PgSchemaValidateArgs> {
        Ok(PgSchemaValidateArgs {
            schema: self
                .args
                .schema
                .clone()
                .ok_or_else(|| anyhow!("schema is required for PgSchema validation"))?,
            data: self.args.data.clone(),
            data_format: self.args.data_format,
            typemap: self
                .args
                .shapemap
                .clone()
                .ok_or_else(|| anyhow!("shapemap is required for PgSchema validation"))?,
            result_validation_format: self.args.result_format.try_into()?,
            common: CommonArgsOutputForceOverWrite {
                output: self.args.common.output.clone(),
                force_overwrite: self.args.common.force_overwrite,
            },
        })
    }
}

impl Command for ValidateCommand {
    /// Returns the unique identifier for this command.
    fn name(&self) -> &'static str {
        "validate"
    }

    /// Executes the validate logic.
    fn execute(&self, ctx: &mut CommandContext) -> Result<()> {
        match self.args.validation_mode {
            ValidationModeCli::ShEx => {
                let shex_args = self.to_shex_args()?;
                let cmd = ShexValidateCommand::new(shex_args);
                cmd.execute(ctx)
            },
            ValidationModeCli::Shacl => {
                let shacl_args = self.to_shacl_args()?;
                let cmd = ShaclValidateCommand::new(shacl_args);
                cmd.execute(ctx)
            },
            ValidationModeCli::PGSchema => {
                let pgschema_args = self.to_pgschema_args()?;
                let cmd = PgSchemaValidateCommand::new(pgschema_args);
                cmd.execute(ctx)
            },
        }
    }
}
