import Hummingbird
import Logging

/// A type that sets and builds an application that should be ready to run.
public struct AppBuilder {

    // MARK: Properties
    
    private let appName: String
    private let archivesFolder: String
    private let environment: Environment

    // MARK: Initialisers
    
    /// Initialises this type.
    /// - Parameters:
    ///   - appName: A name for the app to build.
    ///   - archivesFolder: A relative path to the location of the *DocC* archive containers.
    public init(
        appName: String,
        archivesFolder: String
    ) {
        self.appName = appName
        self.archivesFolder = archivesFolder
        self.environment = Environment()
    }
    
    // MARK: Functions
    
    /// Sets and builds a ready-to-use application.
    ///
    /// The application this function builds have the following features:
    /// * proxy requests to any available *DocC* archive container in the `archivesFolder` location;
    /// * log any request to any available *DocC* archive container in the application console.
    ///
    /// - Parameter arguments: A set in input parameters used while building the application.
    /// - Returns: A configured, functional application that is ready to use.
    public func callAsFunction(_ arguments: some AppArguments) async throws -> some ApplicationProtocol {
        let logger = logger(level: arguments.logLevel)
        let router = router(logger: logger)

        return Application(
            router: router,
            configuration: .init(
                address: .hostname(arguments.hostname, port: arguments.port),
                serverName: appName
            ),
            logger: logger
        )
    }

}

// MARK: - Helpers

private extension AppBuilder {

    // MARK: Type aliases
    
    typealias AppRequestContext = BasicRequestContext

    // MARK: Functions
    
    func logger(level: Logger.Level?) -> Logger {
        var logger = Logger(label: appName)
        
        logger.logLevel = level
            ?? environment.logLevel.flatMap { Logger.Level(rawValue: $0) ?? .info }
            ?? .info
        
        return logger
    }
    
    func router(logger: Logger) -> Router<AppRequestContext> {
        let router = Router()

        router.addMiddleware {
            LogRequestsMiddleware(logger.logLevel)
            DocCMiddleware(archivesFolder)
        }

        return router
    }

}
