import Hummingbird
import HummingbirdTesting
import Testing

@testable import AppLibrary

@Suite("DocCMiddleware", .tags(.middleware))
struct DoccMiddlewareTests {

    @Test(arguments: zip([String].urisRedirect, [String].pathsRedirect))
    func redirects(
        from uri: String,
        to path: String
    ) async throws {
        // GIVEN
        let router = Router.test()
        let app = Application(router: router)

        // WHEN
        // THEN
        try await app.test(.router) { client in
            try await client.execute(uri: uri, method: .get) { response in
                #expect(response.status == .seeOther)
                #expect(response.headers[.location] == path)
            }
        }
    }
    
    @Test(arguments: zip([String].urisServes, [String].identifiersServes))
    func servesFile(
        for uri: String,
        with identifier: String
    ) async throws {
        // GIVEN
        var provider = FileProviderMock()
        
        provider.setFile(identifier: identifier)

        let router = Router.test(assetProvider: provider)
        let app = Application(router: router)

        // WHEN
        // THEN
        try await app.test(.router) { client in
            try await client.execute(uri: uri, method: .get) { response in
                #expect(response.status == .ok)
                #expect(response.body == ByteBuffer(string: identifier))
            }
        }
    }
    
    @Test(arguments: zip([String].urisInvalid, [HTTPResponse.Status].statusesInvalid))
    func throwError(
        for uri: String,
        with status: HTTPResponse.Status
    ) async throws {
        // GIVEN
        let router = Router.test()
        let app = Application(router: router)
        
        // WHEN
        // THEN
        try await app.test(.router) { client in
            try await client.execute(uri: uri, method: .get) { response in
                #expect(response.status == status)
            }
        }
    }

}

// MARK: - Router+Constants

private extension Router<BasicRequestContext> {
    
    // MARK: Functions
    
    static func test(assetProvider: some FileProvider = FileProviderMock()) -> Router {
        let router = Router()
        
        router.addMiddleware {
            DocCMiddleware(assetProvider: assetProvider)
        }
        
        return router
    }
    
}

// MARK: - Collection+String

private extension Collection where Element == String {
    
    // MARK: Computed
    
    static var identifiersServes: [String] {[
        "/SomeArchive.doccarchive/documentation/somearchive/index.html",
        "/SomeArchive.doccarchive/tutorials/somearchive/index.html",
        "/SomeArchive.doccarchive/favicon.ico",
        "/SomeArchive.doccarchive/favicon.svg",
        "/SomeArchive.doccarchive/theme-settings.json",
        "/SomeArchive.doccarchive/data/documentation/somearchive.json",
        "/SomeArchive.doccarchive/css/some-css-file.css",
        "/SomeArchive.doccarchive/data/some-data-file.bin",
        "/SomeArchive.doccarchive/downloads/some-download-file",
        "/SomeArchive.doccarchive/images/some-image-file.jpg",
        "/SomeArchive.doccarchive/img/some-image-file.png",
        "/SomeArchive.doccarchive/index/some-index-file",
        "/SomeArchive.doccarchive/js/some-js-file.js",
        "/SomeArchive.doccarchive/videos/some-video-file.mp4",
    ]}
        

    static var pathsRedirect: [String] {[
        "/archives/SomeArchive/documentation",
        "/archives/SomeArchive/documentation/",
        "/archives/SomeArchive/tutorials/",
    ]}

    static var urisInvalid: [String] {[
        "",
        "some-path",
        "some/uri/path",
        "../",
        "/../",
        "/archives",
        "/archives/SomeArchive/favicon.ico",
        "/archives/SomeArchive/favicon.svg",
        "/archives/SomeArchive/theme-settings.json",
        "/archives/SomeArchive/data/documentation.json",
        "/archives/SomeArchive/css/some-css-file.css",
        "/archives/SomeArchive/data/some-data-file.bin",
        "/archives/SomeArchive/downloads/some-download-file",
        "/archives/SomeArchive/images/some-image-file.jpg",
        "/archives/SomeArchive/img/some-image-file.png",
        "/archives/SomeArchive/index/some-index-file",
        "/archives/SomeArchive/js/some-js-file.js",
        "/archives/SomeArchive/videos/some-video-file.mp4",
        "/archives/SomeArchive/index.html",
        "/archives/SomeArchive/xxx",
        "/archives/SomeArchive/xxx/index.html"
    ]}

    static var urisRedirect: [String] {[
        "/archives/SomeArchive/",
        "/archives/SomeArchive/documentation",
        "/archives/SomeArchive/tutorials",
    ]}
    
    static var urisServes: [String] {[
        "/archives/SomeArchive/documentation/",
        "/archives/SomeArchive/tutorials/",
        "/archives/SomeArchive/favicon.ico",
        "/archives/SomeArchive/favicon.svg",
        "/archives/SomeArchive/theme-settings.json",
        "/archives/SomeArchive/data/documentation.json",
        "/archives/SomeArchive/css/some-css-file.css",
        "/archives/SomeArchive/data/some-data-file.bin",
        "/archives/SomeArchive/downloads/some-download-file",
        "/archives/SomeArchive/images/some-image-file.jpg",
        "/archives/SomeArchive/img/some-image-file.png",
        "/archives/SomeArchive/index/some-index-file",
        "/archives/SomeArchive/js/some-js-file.js",
        "/archives/SomeArchive/videos/some-video-file.mp4",
    ]}
        
}

// MARK: - Collection+HTTPResponse.Status

private extension Collection where Element == HTTPResponse.Status {
    
    // MARK: Computed
    
    static var statusesInvalid: [HTTPResponse.Status] {[
        .notFound,
        .badRequest,
        .badRequest,
        .badRequest,
        .badRequest,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notFound,
        .notImplemented,
        .notImplemented,
        .notImplemented
    ]}
    
}
