package de.effigenix.infrastructure.web.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * OpenAPI/Swagger Configuration.
 *
 * Provides API documentation via Swagger UI.
 * Accessible at: http://localhost:8080/swagger-ui/index.html
 *
 * API Documentation:
 * - OpenAPI 3.0 specification
 * - Interactive API testing via Swagger UI
 * - JWT Bearer token authentication
 *
 * Security:
 * - Swagger UI is PUBLIC in development
 * - IMPORTANT: Restrict access in production!
 *
 * Infrastructure Layer → API Documentation
 */
@Configuration
@OpenAPIDefinition(
        info = @Info(
                title = "Effigenix Fleischerei ERP API",
                version = "0.1.0",
                description = """
                        RESTful API for Effigenix Fleischerei ERP System.

                        ## Authentication

                        All endpoints (except /api/auth/login and /api/auth/refresh) require JWT authentication.

                        1. Login via POST /api/auth/login with username and password
                        2. Copy the returned access token
                        3. Click "Authorize" button (top right)
                        4. Enter: Bearer <access-token>
                        5. Click "Authorize"

                        ## User Management

                        - **Authentication**: Login, logout, refresh token
                        - **User Management**: Create, update, list users (ADMIN only)
                        - **Role Management**: Assign roles, lock/unlock users (ADMIN only)
                        - **Password Management**: Change password (requires current password)

                        ## Error Handling

                        All errors return a consistent error response format:

                        ```json
                        {
                          "code": "USER_NOT_FOUND",
                          "message": "User with ID 'user-123' not found",
                          "status": 404,
                          "timestamp": "2026-02-17T12:00:00",
                          "path": "/api/users/user-123",
                          "validationErrors": null
                        }
                        ```

                        ## Architecture

                        Built with:
                        - Domain-Driven Design (DDD)
                        - Clean Architecture (Hexagonal Architecture)
                        - Spring Boot 3.2
                        - Java 21
                        - PostgreSQL
                        """,
                contact = @Contact(
                        name = "Effigenix Development Team",
                        email = "dev@effigenix.com",
                        url = "https://effigenix.com"
                ),
                license = @License(
                        name = "Proprietary",
                        url = "https://effigenix.com/license"
                )
        ),
        servers = {
                @Server(
                        url = "http://localhost:8080",
                        description = "Local Development Server"
                ),
                @Server(
                        url = "https://api.effigenix.com",
                        description = "Production Server"
                )
        }
)
@SecurityScheme(
        name = "Bearer Authentication",
        type = SecuritySchemeType.HTTP,
        scheme = "bearer",
        bearerFormat = "JWT",
        description = """
                JWT authentication token obtained from POST /api/auth/login.

                Format: Bearer <access-token>

                Example:
                Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
                """
)
public class OpenApiConfig {

    /**
     * Marks fields as required for application-layer DTOs that cannot carry
     * {@code @Schema} annotations (to preserve the clean architecture boundary).
     */
    @Bean
    public OpenApiCustomizer applicationDtoRequiredFieldsCustomizer() {
        // Application-layer DTOs → all required except explicitly nullable
        Map<String, Set<String>> nullableFields = Map.of(
                "UserDTO", Set.of("branchId", "lastLogin"),
                "RoleDTO", Set.of()
        );

        return openApi -> {
            var schemas = openApi.getComponents().getSchemas();
            if (schemas == null) return;

            nullableFields.forEach((schemaName, nullable) -> {
                var schema = schemas.get(schemaName);
                if (schema == null || schema.getProperties() == null) return;

                List<String> required = new ArrayList<>();
                schema.getProperties().keySet().forEach(prop -> {
                    if (!nullable.contains(prop)) {
                        required.add((String) prop);
                    }
                });
                schema.setRequired(required);
            });
        };
    }
}
