use super::{
    BuildMetadata,
    PreReleaseIdentifier,
    SemVer,
};
use crate::error::ZervError;
use crate::version::zerv::core::PreReleaseLabel;
use crate::version::zerv::{
    Component,
    PreReleaseVar,
    Var,
    Zerv,
    ZervSchema,
    ZervVars,
};

struct PreReleaseProcessor<'a> {
    vars: &'a mut ZervVars,
    schema: &'a mut ZervSchema,
    pending_var: Option<Var>,
}

impl<'a> PreReleaseProcessor<'a> {
    fn new(vars: &'a mut ZervVars, schema: &'a mut ZervSchema) -> Self {
        Self {
            vars,
            schema,
            pending_var: None,
        }
    }

    fn is_var_set(&self, var: &Var) -> bool {
        match var {
            Var::PreRelease => self.vars.pre_release.is_some(),
            Var::Epoch => self.vars.epoch.is_some(),
            Var::Post => self.vars.post.is_some(),
            Var::Dev => self.vars.dev.is_some(),
            _ => false,
        }
    }

    fn finalize_var(&mut self, var: Var, value: Option<u64>) -> Result<(), ZervError> {
        match var {
            Var::Epoch => self.vars.epoch = value,
            Var::Post => self.vars.post = value,
            Var::Dev => self.vars.dev = value,
            Var::PreRelease => {
                if let Some(ref mut pr) = self.vars.pre_release {
                    pr.number = value;
                }
            }
            _ => {}
        }
        self.schema.push_extra_core(Component::Var(var))
    }

    fn add_string(&mut self, s: &str) -> Result<(), ZervError> {
        self.schema.push_extra_core(Component::Str(s.to_string()))
    }

    fn handle_duplicate(&mut self, s: &str, var: Var) -> Result<bool, ZervError> {
        if self.pending_var.as_ref() == Some(&var) {
            let pending = self.pending_var.take().unwrap();
            self.finalize_var(pending, None)?;
        } else if self.is_var_set(&var) {
            if let Some(pending) = self.pending_var.take() {
                self.finalize_var(pending, None)?;
            }
        } else {
            return Ok(false);
        }
        self.add_string(s)?;
        Ok(true)
    }

    fn process_new_var(&mut self, s: &str, var: Var) -> Result<(), ZervError> {
        if var == Var::PreRelease {
            if let Some(label) = PreReleaseLabel::try_from_str(s) {
                self.vars.pre_release = Some(PreReleaseVar {
                    label,
                    number: None,
                });
                self.pending_var = Some(var);
                return Ok(());
            }
        } else {
            self.pending_var = Some(var);
            return Ok(());
        }
        self.add_string(s)
    }
}

impl From<SemVer> for Zerv {
    fn from(semver: SemVer) -> Self {
        let schema = ZervSchema::semver_default().expect("SemVer default schema should be valid");
        semver
            .to_zerv_with_schema(&schema)
            .expect("SemVer default conversion should work")
    }
}

impl SemVer {
    fn process_string_identifier(
        processor: &mut PreReleaseProcessor,
        s: &str,
    ) -> Result<(), ZervError> {
        // Special case: pending PreRelease var with another string
        if processor.pending_var == Some(Var::PreRelease) {
            processor.finalize_var(Var::PreRelease, None)?;
            processor.pending_var = None;
            processor.add_string(s)?;
            return Ok(());
        }

        // Handle duplicates or finalize pending vars
        if let Some(var) = Var::try_from_secondary_label(s)
            && processor.handle_duplicate(s, var)?
        {
            return Ok(());
        }

        // Finalize any pending var before processing new one
        if let Some(pending) = processor.pending_var.take() {
            processor.finalize_var(pending, None)?;
        }

        // Process new var or add as string
        if let Some(var) = Var::try_from_secondary_label(s) {
            processor.process_new_var(s, var)?;
        } else {
            processor.add_string(s)?;
        }
        Ok(())
    }

    fn process_uint_identifier(
        processor: &mut PreReleaseProcessor,
        n: u64,
    ) -> Result<(), ZervError> {
        if let Some(var) = processor.pending_var.take() {
            processor.finalize_var(var, Some(n))?;
        } else {
            processor.schema.push_extra_core(Component::UInt(n))?;
        }
        Ok(())
    }

    fn process_pre_release(
        processor: &mut PreReleaseProcessor,
        pre_release: &[PreReleaseIdentifier],
    ) -> Result<(), ZervError> {
        for identifier in pre_release {
            match identifier {
                PreReleaseIdentifier::Str(s) => {
                    Self::process_string_identifier(processor, s)?;
                }
                PreReleaseIdentifier::UInt(n) => {
                    Self::process_uint_identifier(processor, *n)?;
                }
            }
        }
        Ok(())
    }

    fn process_build_metadata(
        schema: &mut ZervSchema,
        build_metadata: &[BuildMetadata],
    ) -> Result<(), ZervError> {
        for metadata in build_metadata {
            let component = match metadata {
                BuildMetadata::Str(s) => Component::Str(s.clone()),
                BuildMetadata::UInt(n) => Component::UInt(*n),
            };
            schema.push_build(component)?;
        }
        Ok(())
    }

    /// Convert SemVer to Zerv format while preserving all semantic information for round-trip conversion.
    pub fn to_zerv_with_schema(&self, schema: &ZervSchema) -> Result<Zerv, ZervError> {
        if *schema != ZervSchema::semver_default()? {
            return Err(ZervError::NotImplemented(
                "Custom schemas not yet implemented for SemVer conversion".to_string(),
            ));
        }

        let mut vars = ZervVars {
            major: Some(self.major),
            minor: Some(self.minor),
            patch: Some(self.patch),
            ..Default::default()
        };

        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        if let Some(pre_release) = &self.pre_release {
            Self::process_pre_release(&mut processor, pre_release)?;
        }

        if let Some(var) = processor.pending_var.take() {
            processor.schema.push_extra_core(Component::Var(var))?;
        }

        if let Some(build_metadata) = &self.build_metadata {
            Self::process_build_metadata(&mut result_schema, build_metadata)?;
        }

        Ok(Zerv {
            vars,
            schema: result_schema,
        })
    }
}

#[cfg(test)]
mod tests {
    use rstest::rstest;

    use super::*;
    use crate::test_utils::zerv::zerv_semver::to;
    use crate::version::zerv::{
        PrecedenceOrder,
        Zerv,
    };

    #[rstest]
    #[case("1.2.3", to::v1_2_3().build())]
    #[case("1.0.0-alpha.1", to::v1_0_0_a1().build())]
    #[case("1.0.0-something.1", to::v1_0_0_something_1().build())]
    #[case("1.0.0+build.123", to::v1_0_0_build().build())]
    #[case("1.0.0-alpha.1+build.123", to::v1_0_0_a1_build().build())]
    #[case(
        "1.0.0-alpha.1.lowercase.4.UPPERCASE.5+build.123",
        to::v1_0_0_a1_complex().build()
    )]
    #[case("1.0.0-foo.bar.beta.2.baz", to::v1_0_0_foo_bar_beta_2_baz().build())]
    #[case("1.0.0-alpha.1.beta.2", to::v1_0_0_alpha_1_beta_2().build())]
    #[case("1.0.0-rc.1.alpha.2.beta.3", to::v1_0_0_rc_1_alpha_2_beta_3().build())]
    #[case("1.0.0-pre.alpha.1", to::v1_0_0_pre_alpha_1().build())]
    #[case("1.0.0-test.alpha.beta.rc.1", to::v1_0_0_test_alpha_beta_rc_1().build())]
    #[case("1.0.0-ALPHA.1", to::v1_0_0_alpha_1().build())]
    #[case("1.0.0-epoch.1", to::v1_0_0_epoch_1().build())]
    #[case("1.0.0-post.1", to::v1_0_0_post_1().build())]
    #[case("1.0.0-dev.1", to::v1_0_0_dev_1().build())]
    // Case variations - Beta
    #[case("1.0.0-BETA.2", to::v1_0_0_beta_2().build())]
    // Case variations - RC
    #[case("1.0.0-RC.3", to::v1_0_0_rc_3().build())]
    // Case variations - Preview (treated as regular string)
    #[case("1.0.0-Preview.4", to::v1_0_0_preview_4().build())]
    // Case variations - short forms
    #[case("1.0.0-a.1", to::v1_0_0_a_1().build())]
    #[case("1.0.0-b.2", to::v1_0_0_b_2().build())]
    #[case("1.0.0-c.3", to::v1_0_0_c_3().build())]
    // Case variations - without numbers
    #[case("1.0.0-alpha", to::v1_0_0_alpha().build())]
    #[case("1.0.0-beta", to::v1_0_0_beta().build())]
    #[case("1.0.0-rc", to::v1_0_0_rc().build())]
    // Case variations - with zero
    #[case("1.0.0-alpha.0", to::v1_0_0_alpha_0().build())]
    #[case("1.0.0-beta.0", to::v1_0_0_beta_0().build())]
    // Case variations - with prefix (alpha/beta found later in sequence)
    #[case("1.0.0-foo.1.alpha", to::v1_0_0_foo_1_alpha().build())]
    #[case("1.0.0-bar.2.beta", to::v1_0_0_bar_2_beta().build())]
    // Epoch handling
    #[case("1.0.0-epoch.1", to::v1_0_0_epoch_1().build())]
    #[case("1.0.0-epoch.5", to::v1_0_0_epoch_5().build())]
    #[case("1.0.0-epoch.0", to::v1_0_0_epoch_0().build())]
    #[case("1.0.0-epoch.999", to::v1_0_0_epoch_999().build())]
    // Post handling
    #[case("1.0.0-post.1", to::v1_0_0_post_1().build())]
    #[case("1.0.0-post.5", to::v1_0_0_post_5().build())]
    #[case("1.0.0-post.0", to::v1_0_0_post_0().build())]
    // Dev handling
    #[case("1.0.0-dev.1", to::v1_0_0_dev_1().build())]
    #[case("1.0.0-dev.0", to::v1_0_0_dev_0().build())]
    #[case("1.0.0-dev.10", to::v1_0_0_dev_10().build())]
    // Complex combinations
    #[case("1.0.0-epoch.2.alpha.1", to::v1_0_0_epoch_2_alpha_1().build())]
    #[case("1.0.0-epoch.3.beta.2", to::v1_0_0_epoch_3_beta_2().build())]
    #[case("1.0.0-epoch.1.rc.5", to::v1_0_0_epoch_1_rc_5().build())]
    #[case("1.0.0-epoch.4.alpha", to::v1_0_0_epoch_4_alpha().build())]
    #[case("1.0.0-post.1.dev.2", to::v1_0_0_post_1_dev_2().build())]
    #[case("1.0.0-dev.3.post.4", to::v1_0_0_dev_3_post_4().build())]
    #[case("1.0.0-alpha.1.post.2", to::v1_0_0_alpha_1_post_2().build())]
    #[case("1.0.0-beta.3.post.1", to::v1_0_0_beta_3_post_1().build())]
    #[case("1.0.0-rc.2.post.5", to::v1_0_0_rc_2_post_5().build())]
    #[case("1.0.0-alpha.1.dev.2", to::v1_0_0_alpha_1_dev_2().build())]
    #[case("1.0.0-beta.2.dev.1", to::v1_0_0_beta_2_dev_1().build())]
    #[case("1.0.0-rc.1.dev.3", to::v1_0_0_rc_1_dev_3().build())]
    #[case("1.0.0-alpha.1.post.2.dev.3", to::v1_0_0_alpha_1_post_2_dev_3().build())]
    #[case("1.0.0-beta.2.dev.1.post.3", to::v1_0_0_beta_2_dev_1_post_3().build())]
    #[case("1.0.0-rc.1.post.1.dev.1", to::v1_0_0_rc_1_post_1_dev_1().build())]
    #[case("1.0.0-epoch.2.post.1.dev.3", to::v1_0_0_epoch_2_post_1_dev_3().build())]
    #[case("1.0.0-epoch.1.dev.2.post.1", to::v1_0_0_epoch_1_dev_2_post_1().build())]
    #[case("1.0.0-epoch.3.alpha.1.post.2.dev.1", to::v1_0_0_epoch_3_alpha_1_post_2_dev_1().build())]
    #[case("1.0.0-epoch.1.beta.2.dev.3.post.1", to::v1_0_0_epoch_1_beta_2_dev_3_post_1().build())]
    // Build metadata combinations
    #[case("1.0.0-epoch.1+build.123", to::v1_0_0_epoch_1_build().build())]
    #[case("1.0.0-post.1+build.456", to::v1_0_0_post_1_build().build())]
    #[case("1.0.0-dev.2+build.789", to::v1_0_0_dev_2_build().build())]
    #[case("1.0.0-epoch.2.alpha.1+build.abc", to::v1_0_0_epoch_2_alpha_1_build().build())]
    // Mixed order cases
    #[case("1.0.0-foo.epoch.1.alpha.2", to::v1_0_0_foo_epoch_1_alpha_2().build())]
    #[case("1.0.0-epoch.1.foo.post.2", to::v1_0_0_epoch_1_foo_post_2().build())]
    #[case("1.0.0-bar.dev.1.epoch.2", to::v1_0_0_bar_dev_1_epoch_2().build())]
    // Complex duplicate
    #[case("1.0.0-epoch.1.epoch.2.post.3.post.4.dev.5.dev.6.alpha.7.alpha.8", to::v1_0_0_duplicate_vars().build())]
    #[case("1.0.0-epoch.epoch.rc.rc.post.post.dev.dev", to::v1_0_0_duplicate_vars_without_num().build())]
    #[case("1.2.3-10.a.rc.epoch.rc.3", to::v1_2_3_complex_duplicate().build())]
    fn test_semver_to_zerv_conversion(#[case] semver_str: &str, #[case] expected: Zerv) {
        let semver: SemVer = semver_str.parse().unwrap();
        let zerv: Zerv = semver.into();
        assert_eq!(zerv, expected);
    }

    #[rstest]
    #[case("1.0.0")]
    #[case("2.1.0-beta.1")]
    #[case("1.0.0+build.123")]
    #[case("2.1.0-beta.1+build.123")]
    #[case("1.0.0-alpha.1.post.2.dev.3")]
    fn test_round_trip_conversion(#[case] version_str: &str) {
        let original: SemVer = version_str.parse().unwrap();
        let zerv: Zerv = original.clone().into();
        let converted: SemVer = zerv.into();

        assert_eq!(original.to_string(), converted.to_string());
    }

    #[test]
    fn test_custom_schema_not_supported() {
        let semver: SemVer = "1.2.3".parse().unwrap();
        let custom_schema = ZervSchema::new_with_precedence(
            vec![Component::Var(Var::Major)],
            vec![],
            vec![],
            PrecedenceOrder::default(),
        )
        .unwrap();
        let result = semver.to_zerv_with_schema(&custom_schema);
        assert!(result.is_err());
        match result {
            Err(ZervError::NotImplemented(msg)) => {
                assert!(msg.contains("Custom schemas"));
            }
            _ => panic!("Expected NotImplemented error"),
        }
    }

    #[test]
    fn test_pre_release_processor_edge_cases() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // Test is_var_set with non-special vars (line 38)
        assert!(!processor.is_var_set(&Var::Major));
        assert!(!processor.is_var_set(&Var::Minor));
        assert!(!processor.is_var_set(&Var::Patch));
        // Build doesn't exist as a Var, use a custom var that doesn't match the special cases
        assert!(!processor.is_var_set(&Var::Custom(String::from("test"))));
    }

    #[test]
    fn test_finalize_var_with_pre_release_number() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars {
            pre_release: Some(PreReleaseVar {
                label: PreReleaseLabel::Alpha,
                number: None,
            }),
            ..Default::default()
        };
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute line 49
        processor.finalize_var(Var::PreRelease, Some(5)).unwrap();
        assert_eq!(vars.pre_release.unwrap().number, Some(5));
    }

    #[test]
    fn test_finalize_var_with_unknown_var() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute line 52 (the _ => {} branch)
        processor
            .finalize_var(Var::Custom(String::from("unknown")), Some(123))
            .unwrap();
        // Should not crash and should add to schema
    }

    #[test]
    fn test_handle_duplicate_with_var_set_and_pending() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars {
            epoch: Some(1), // Var is already set
            ..Default::default()
        };
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);
        processor.pending_var = Some(Var::Post); // Different pending var

        // This should execute line 67 (finalize pending when var is set)
        let result = processor.handle_duplicate("epoch", Var::Epoch);
        assert!(result.unwrap());
        assert!(processor.pending_var.is_none());
    }

    #[test]
    fn test_process_new_var_with_pre_release_success() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute lines 79-84
        processor.process_new_var("alpha", Var::PreRelease).unwrap();
        drop(processor); // Release the mutable borrow
        assert!(vars.pre_release.is_some());
        assert_eq!(vars.pre_release.unwrap().label, PreReleaseLabel::Alpha);
    }

    #[test]
    fn test_process_new_var_with_pre_release_fallback() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute line 90 (add_string when pre-release label not recognized)
        processor
            .process_new_var("unknown", Var::PreRelease)
            .unwrap();
        drop(processor); // Release the mutable borrow
        assert!(vars.pre_release.is_none());
    }

    #[test]
    fn test_process_new_var_with_non_pre_release() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute lines 87-88
        processor
            .process_new_var("some_string", Var::Epoch)
            .unwrap();
        assert_eq!(processor.pending_var, Some(Var::Epoch));
    }

    #[test]
    fn test_process_string_identifier_with_pending_pre_release() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);
        processor.pending_var = Some(Var::PreRelease);

        // This should execute lines 109-113
        SemVer::process_string_identifier(&mut processor, "additional_string").unwrap();
        assert!(processor.pending_var.is_none());
    }

    #[test]
    fn test_process_string_identifier_finalize_pending() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);
        processor.pending_var = Some(Var::Epoch);

        // This should execute lines 124-125
        SemVer::process_string_identifier(&mut processor, "some_string").unwrap();
        assert!(processor.pending_var.is_none());
    }

    #[test]
    fn test_process_string_identifier_with_new_var() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);

        // This should execute line 130
        SemVer::process_string_identifier(&mut processor, "alpha").unwrap();
        drop(processor); // Release the mutable borrow
        assert!(vars.pre_release.is_some());
    }

    #[test]
    fn test_process_uint_identifier_with_pending_var() {
        let schema = ZervSchema::semver_default().unwrap();
        let mut vars = ZervVars::default();
        let mut result_schema = schema.clone();
        let mut processor = PreReleaseProcessor::new(&mut vars, &mut result_schema);
        processor.pending_var = Some(Var::Epoch);

        // This should execute line 142
        SemVer::process_uint_identifier(&mut processor, 5).unwrap();
        drop(processor); // Release the mutable borrow
        assert_eq!(vars.epoch, Some(5));
    }

    #[test]
    fn test_process_string_identifier_calls_process_string() {
        let schema = ZervSchema::semver_default().unwrap();

        // Create a pre-release with string identifier to trigger line 156
        // Use a valid pre-release format with a non-recognized label
        let semver: SemVer = "1.0.0-customlabel".parse().unwrap();
        let zerv = semver.to_zerv_with_schema(&schema).unwrap();
        assert!(
            zerv.schema
                .extra_core()
                .iter()
                .any(|c| matches!(c, Component::Str(s) if s == "customlabel"))
        );
    }

    #[test]
    fn test_to_zerv_with_schema_with_pre_release() {
        let semver: SemVer = "1.2.3-alpha.1".parse().unwrap();
        let schema = ZervSchema::semver_default().unwrap();

        // This should execute line 199
        let result = semver.to_zerv_with_schema(&schema);
        assert!(result.is_ok());
        let zerv = result.unwrap();
        assert!(zerv.vars.pre_release.is_some());
    }

    #[test]
    fn test_to_zerv_with_schema_pending_var() {
        let semver: SemVer = "1.2.3-alpha".parse().unwrap(); // No number after alpha
        let schema = ZervSchema::semver_default().unwrap();

        // This should execute line 203 (finalize pending var)
        let result = semver.to_zerv_with_schema(&schema);
        assert!(result.is_ok());
        let zerv = result.unwrap();
        assert!(
            zerv.schema
                .extra_core()
                .iter()
                .any(|c| matches!(c, Component::Var(Var::PreRelease)))
        );
    }

    #[test]
    fn test_to_zerv_with_schema_build_metadata() {
        let semver: SemVer = "1.2.3+build.123".parse().unwrap();
        let schema = ZervSchema::semver_default().unwrap();

        // This should execute line 207
        let result = semver.to_zerv_with_schema(&schema);
        assert!(result.is_ok());
        let zerv = result.unwrap();
        assert!(!zerv.schema.build().is_empty());
    }
}
