//! SEFAZ web service operation metadata.
//!
//! Maps each NF-e/NFC-e web service to its SOAP method name, WSDL operation
//! identifier, and XML schema version — the three pieces needed to build the
//! SOAP envelope and `Content-Type` action header.

/// SOAP metadata for a single SEFAZ web service.
///
/// Constructed only via [`SefazService::meta`]; the fields are read-only to
/// external crates thanks to `#[non_exhaustive]`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct ServiceMeta {
    /// SOAP method name (e.g. `"nfeStatusServicoNF"`).
    pub method: &'static str,
    /// WSDL operation identifier used to build the namespace
    /// (e.g. `"NFeStatusServico4"`).
    pub operation: &'static str,
    /// XML schema version sent in `<versaoDados>` (e.g. `"4.00"`).
    pub version: &'static str,
}

/// NF-e/NFC-e SEFAZ web service operations.
///
/// Each variant maps to one WSDL endpoint and carries fixed SOAP metadata
/// retrievable via [`meta()`](SefazService::meta).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SefazService {
    /// `NFeStatusServico4` — check SEFAZ operational status.
    StatusServico,
    /// `NFeAutorizacao4` — submit NF-e batch for authorization.
    Autorizacao,
    /// `NFeRetAutorizacao4` — query batch result by receipt number.
    RetAutorizacao,
    /// `NFeConsultaProtocolo4` — consult NF-e by 44-digit access key.
    ConsultaProtocolo,
    /// `NFeInutilizacao4` — void an unused invoice number range.
    Inutilizacao,
    /// `NFeRecepcaoEvento4` — submit events (cancel, CCe, manifest, …).
    RecepcaoEvento,
    /// `NFeDistribuicaoDFe` — distribute fiscal documents (DF-e).
    DistribuicaoDFe,
    /// `CadConsultaCadastro4` — query taxpayer registration.
    ConsultaCadastro,
    /// `CscNFCe` — manage CSC (Código de Segurança do Contribuinte) for NFC-e.
    CscNFCe,
    /// `RecepcaoEPEC` — submit EPEC contingency events for NFC-e (SP only).
    RecepcaoEPEC,
    /// `EPECStatusServico` — check EPEC service status for NFC-e (SP only).
    EPECStatusServico,
    /// `RecepcaoEPEC` — submit EPEC events for NFC-e (SP only).
    RecepcaoEpecNfce,
    /// `EPECStatusServico` — check EPEC NFC-e service status (SP only).
    EpecNfceStatusServico,
    /// `NfeConsultaDest` — query NF-e destined to a taxpayer (AN only).
    NfeConsultaDest,
    /// `NfeDownloadNF` — download NF-e XML (AN only).
    NfeDownloadNF,
}

impl SefazService {
    /// Return the SOAP metadata for this service.
    ///
    /// # Examples
    ///
    /// ```
    /// use fiscal_sefaz::services::SefazService;
    ///
    /// let meta = SefazService::StatusServico.meta();
    /// assert_eq!(meta.method, "nfeStatusServicoNF");
    /// assert_eq!(meta.operation, "NFeStatusServico4");
    /// assert_eq!(meta.version, "4.00");
    /// ```
    pub fn meta(self) -> ServiceMeta {
        match self {
            Self::StatusServico => ServiceMeta {
                method: "nfeStatusServicoNF",
                operation: "NFeStatusServico4",
                version: "4.00",
            },
            Self::Autorizacao => ServiceMeta {
                method: "nfeAutorizacaoLote",
                operation: "NFeAutorizacao4",
                version: "4.00",
            },
            Self::RetAutorizacao => ServiceMeta {
                method: "nfeRetAutorizacaoLote",
                operation: "NFeRetAutorizacao4",
                version: "4.00",
            },
            Self::ConsultaProtocolo => ServiceMeta {
                method: "nfeConsultaNF",
                operation: "NFeConsultaProtocolo4",
                version: "4.00",
            },
            Self::Inutilizacao => ServiceMeta {
                method: "nfeInutilizacaoNF",
                operation: "NFeInutilizacao4",
                version: "4.00",
            },
            Self::RecepcaoEvento => ServiceMeta {
                method: "nfeRecepcaoEvento",
                operation: "NFeRecepcaoEvento4",
                version: "1.00",
            },
            Self::DistribuicaoDFe => ServiceMeta {
                method: "nfeDistDFeInteresse",
                operation: "NFeDistribuicaoDFe",
                version: "1.01",
            },
            Self::ConsultaCadastro => ServiceMeta {
                method: "consultaCadastro",
                operation: "CadConsultaCadastro4",
                version: "2.00",
            },
            Self::CscNFCe => ServiceMeta {
                method: "admCscNFCe",
                operation: "CscNFCe",
                version: "1.00",
            },
            Self::RecepcaoEPEC => ServiceMeta {
                method: "nfeRecepcaoEvento",
                operation: "RecepcaoEvento",
                version: "1.00",
            },
            Self::EPECStatusServico => ServiceMeta {
                method: "nfeStatusServicoNF2",
                operation: "NfeStatusServico2",
                version: "4.00",
            },
            Self::RecepcaoEpecNfce => ServiceMeta {
                method: "nfeRecepcaoEvento",
                operation: "RecepcaoEvento",
                version: "1.00",
            },
            Self::EpecNfceStatusServico => ServiceMeta {
                method: "nfeStatusServicoNF2",
                operation: "NfeStatusServico2",
                version: "4.00",
            },
            Self::NfeConsultaDest => ServiceMeta {
                method: "nfeConsultaNFDest",
                operation: "NfeConsultaDest",
                version: "1.01",
            },
            Self::NfeDownloadNF => ServiceMeta {
                method: "nfeDownloadNF",
                operation: "NfeDownloadNF",
                version: "4.00",
            },
        }
    }

    /// Service name used as lookup key in [`crate::urls::get_sefaz_url`].
    pub fn url_key(self) -> &'static str {
        match self {
            Self::StatusServico => "NfeStatusServico",
            Self::Autorizacao => "NfeAutorizacao",
            Self::RetAutorizacao => "NfeRetAutorizacao",
            Self::ConsultaProtocolo => "NfeConsultaProtocolo",
            Self::Inutilizacao => "NfeInutilizacao",
            Self::RecepcaoEvento => "RecepcaoEvento",
            Self::DistribuicaoDFe => "NfeDistribuicaoDFe",
            Self::ConsultaCadastro => "NfeConsultaCadastro",
            Self::CscNFCe => "CscNFCe",
            Self::RecepcaoEPEC => "RecepcaoEPEC",
            Self::EPECStatusServico => "EPECStatusServico",
            Self::RecepcaoEpecNfce => "RecepcaoEPEC",
            Self::EpecNfceStatusServico => "EPECStatusServico",
            Self::NfeConsultaDest => "NfeConsultaDest",
            Self::NfeDownloadNF => "NfeDownloadNF",
        }
    }
}

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

    #[test]
    fn all_services_have_non_empty_meta() {
        let services = [
            SefazService::StatusServico,
            SefazService::Autorizacao,
            SefazService::RetAutorizacao,
            SefazService::ConsultaProtocolo,
            SefazService::Inutilizacao,
            SefazService::RecepcaoEvento,
            SefazService::DistribuicaoDFe,
            SefazService::ConsultaCadastro,
            SefazService::CscNFCe,
            SefazService::RecepcaoEPEC,
            SefazService::EPECStatusServico,
            SefazService::RecepcaoEpecNfce,
            SefazService::EpecNfceStatusServico,
            SefazService::NfeConsultaDest,
            SefazService::NfeDownloadNF,
        ];
        for svc in services {
            let meta = svc.meta();
            assert!(!meta.method.is_empty(), "{svc:?} has empty method");
            assert!(!meta.operation.is_empty(), "{svc:?} has empty operation");
            assert!(!meta.version.is_empty(), "{svc:?} has empty version");
        }
    }

    #[test]
    fn url_keys_match_urls_module_expectations() {
        // These keys must match the `service` parameter accepted by
        // `urls::get_sefaz_url` — if they drift, URL lookups will fail.
        assert_eq!(SefazService::StatusServico.url_key(), "NfeStatusServico");
        assert_eq!(SefazService::Autorizacao.url_key(), "NfeAutorizacao");
        assert_eq!(SefazService::RetAutorizacao.url_key(), "NfeRetAutorizacao");
        assert_eq!(
            SefazService::ConsultaProtocolo.url_key(),
            "NfeConsultaProtocolo"
        );
        assert_eq!(SefazService::Inutilizacao.url_key(), "NfeInutilizacao");
        assert_eq!(SefazService::RecepcaoEvento.url_key(), "RecepcaoEvento");
    }

    #[test]
    fn meta_versions_are_well_formed() {
        for svc in [
            SefazService::StatusServico,
            SefazService::Autorizacao,
            SefazService::ConsultaProtocolo,
            SefazService::Inutilizacao,
        ] {
            assert_eq!(
                svc.meta().version,
                "4.00",
                "{svc:?} should use version 4.00"
            );
        }
        assert_eq!(SefazService::RecepcaoEvento.meta().version, "1.00");
        assert_eq!(SefazService::DistribuicaoDFe.meta().version, "1.01");
        assert_eq!(SefazService::ConsultaCadastro.meta().version, "2.00");
        assert_eq!(SefazService::CscNFCe.meta().version, "1.00");
        assert_eq!(SefazService::RecepcaoEPEC.meta().version, "1.00");
        assert_eq!(SefazService::EPECStatusServico.meta().version, "4.00");
    }

    #[test]
    fn csc_nfce_meta() {
        let meta = SefazService::CscNFCe.meta();
        assert_eq!(meta.method, "admCscNFCe");
        assert_eq!(meta.operation, "CscNFCe");
        assert_eq!(meta.version, "1.00");
    }

    #[test]
    fn csc_nfce_url_key() {
        assert_eq!(SefazService::CscNFCe.url_key(), "CscNFCe");
    }

    #[test]
    fn recepcao_epec_meta() {
        let meta = SefazService::RecepcaoEPEC.meta();
        assert_eq!(meta.method, "nfeRecepcaoEvento");
        assert_eq!(meta.operation, "RecepcaoEvento");
        assert_eq!(meta.version, "1.00");
    }

    #[test]
    fn recepcao_epec_url_key() {
        assert_eq!(SefazService::RecepcaoEPEC.url_key(), "RecepcaoEPEC");
    }

    #[test]
    fn epec_status_servico_meta() {
        let meta = SefazService::EPECStatusServico.meta();
        assert_eq!(meta.method, "nfeStatusServicoNF2");
        assert_eq!(meta.operation, "NfeStatusServico2");
        assert_eq!(meta.version, "4.00");
    }

    #[test]
    fn epec_status_servico_url_key() {
        assert_eq!(
            SefazService::EPECStatusServico.url_key(),
            "EPECStatusServico"
        );
    }

    #[test]
    fn distribuicao_dfe_url_key() {
        assert_eq!(
            SefazService::DistribuicaoDFe.url_key(),
            "NfeDistribuicaoDFe"
        );
    }

    #[test]
    fn consulta_cadastro_url_key() {
        assert_eq!(
            SefazService::ConsultaCadastro.url_key(),
            "NfeConsultaCadastro"
        );
    }

    #[test]
    fn recepcao_epec_nfce_url_key() {
        assert_eq!(SefazService::RecepcaoEpecNfce.url_key(), "RecepcaoEPEC");
    }

    #[test]
    fn epec_nfce_status_servico_url_key() {
        assert_eq!(
            SefazService::EpecNfceStatusServico.url_key(),
            "EPECStatusServico"
        );
    }

    #[test]
    fn recepcao_epec_nfce_meta() {
        let meta = SefazService::RecepcaoEpecNfce.meta();
        assert_eq!(meta.method, "nfeRecepcaoEvento");
        assert_eq!(meta.operation, "RecepcaoEvento");
        assert_eq!(meta.version, "1.00");
    }

    #[test]
    fn epec_nfce_status_servico_meta() {
        let meta = SefazService::EpecNfceStatusServico.meta();
        assert_eq!(meta.method, "nfeStatusServicoNF2");
        assert_eq!(meta.operation, "NfeStatusServico2");
        assert_eq!(meta.version, "4.00");
    }
}
