use utoipa::openapi::{self, tag::TagBuilder, InfoBuilder};
use utoipa::OpenApi;

pub struct ApiDoc;

impl OpenApi for ApiDoc {
    fn openapi() -> openapi::OpenApi {
        let mut doc = crate::routes::health::HealthApiDoc::openapi();
        doc.merge(crate::routes::auth::AuthApiDoc::openapi());
        doc.merge(crate::routes::documents::DocumentsApiDoc::openapi());
        doc.merge(crate::routes::folders::FoldersApiDoc::openapi());
        doc.merge(crate::routes::tags::TagsApiDoc::openapi());
        doc.merge(crate::routes::correspondents::CorrespondentsApiDoc::openapi());
        doc.merge(crate::routes::profile::ProfileApiDoc::openapi());
        doc.merge(crate::routes::capability_sets::CapabilitySetsApiDoc::openapi());
        doc.merge(crate::routes::tenants::TenantsApiDoc::openapi());

        doc.info = InfoBuilder::new()
            .title("Papercrate API")
            .version(env!("CARGO_PKG_VERSION"))
            .build();

        doc.tags = Some(vec![
            TagBuilder::new()
                .name("Health")
                .description(Some("Service health"))
                .build(),
            TagBuilder::new()
                .name("Auth")
                .description(Some("Authentication"))
                .build(),
            TagBuilder::new()
                .name("Documents")
                .description(Some("Document management"))
                .build(),
            TagBuilder::new()
                .name("Assets")
                .description(Some("Document assets"))
                .build(),
            TagBuilder::new()
                .name("Folders")
                .description(Some("Folder management"))
                .build(),
            TagBuilder::new()
                .name("Tags")
                .description(Some("Tag catalog"))
                .build(),
            TagBuilder::new()
                .name("Correspondents")
                .description(Some("Correspondent catalog"))
                .build(),
            TagBuilder::new()
                .name("Profile")
                .description(Some("User profile and WebDAV tokens"))
                .build(),
            TagBuilder::new()
                .name("Capability Sets")
                .description(Some("Capability set management"))
                .build(),
            TagBuilder::new()
                .name("Tenants")
                .description(Some("Tenant catalog"))
                .build(),
        ]);

        doc
    }
}

pub mod schemas {
    pub use crate::auth::passkeys::{
        AuthenticationChallengeResponse, PasskeyLoginFinishPayload, PasskeyLoginStartPayload,
        PasskeyRegistrationFinishPayload, PasskeySummary, RegistrationChallengeResponse,
    };
    pub use crate::auth::AuthenticatedUser;
    pub use crate::documents::asset::{
        DocumentAssetDetailResponse, DocumentAssetResponse, DocumentVersionDetailResponse,
        DocumentVersionResponse,
    };
    pub use crate::documents::correspondents::DocumentCorrespondentResponse;
    pub use crate::error::ApiErrorResponse;
    pub use crate::models::ApiCapability;
    pub use crate::routes::correspondents::{
        CorrespondentSummary, CorrespondentUsage, CreateCorrespondentRequest,
        UpdateCorrespondentRequest,
    };
    pub use crate::routes::documents::{
        AssetRequestQuery, DocumentCheckQuery, MoveDocumentRequest, RestoreDocumentRequest,
        UploadDocumentForm,
    };
    pub use crate::routes::folders::FolderContentsResponse;
    pub use crate::routes::tags::{CreateTagRequest, TagCatalogEntry, UpdateTagRequest};
    pub use crate::services::auth::{
        ApiTokenExchangeRequest, LoginRequest, LoginResponse, LoginResponseVariants,
        SignupFinishRequest, SignupStartRequest, SignupStartResponse, TenantListResponse,
        TenantSelectionRequest, TenantSelectionResponse, TenantSnippet,
    };
    pub use crate::services::capability_sets::{
        CapabilitySetResponse, CreateCapabilitySetRequest, UpdateCapabilitySetRequest,
    };
    pub use crate::services::correspondents::{
        AssignCorrespondentsRequest, BulkCorrespondentAction, BulkCorrespondentResponse,
        BulkCorrespondentsRequest, CorrespondentAssignmentInput,
    };
    pub use crate::services::documents::{
        BulkMoveRequest, BulkMoveResponse, BulkReanalyzeResponse, BulkReanalyzeSelectionRequest,
        DocumentCheckResponse, DocumentDetailResponse, DocumentListQuery, DocumentMetadataUpdate,
        DocumentResponse, DocumentStatusFilter, TagResponse, UpdateDocumentRequest,
    };
    pub use crate::services::folders::{
        CreateFolderRequest, EnsureFolderPathRequest, FolderContentsQuery, FolderInfo,
        UpdateFolderRequest,
    };
    pub use crate::services::profile::{
        ApiTokenCreatedResponse, ApiTokenResponse, CreateApiTokenRequest, RevokePasskeyQuery,
    };
    pub use crate::services::tags::{
        AssignTagsRequest, BulkTagAction, BulkTagRequest, BulkTagResponse,
    };
    pub use crate::services::tenants::{
        TenantUserListResponse, TenantUserSummary, UpdateTenantRequest, UpdateTenantUserRequest,
    };
}

#[cfg(test)]
mod tests {
    use super::ApiDoc;
    use utoipa::OpenApi;

    #[test]
    fn openapi_serializes() {
        let spec = ApiDoc::openapi();
        let _ = serde_json::to_string(&spec).expect("serialize openapi");
    }
}
