// Copyright © 2024 Ryan Booker. All rights reserved.

import ArgumentParser
import Domain
import Foundation

public struct FastGPT: AsyncParsableCommand {
    public static let configuration = CommandConfiguration(
        commandName: "fastgpt",
        abstract: "Ask FastGPT something.",
        usage: "kg fastgpt \"What is the answer to life, the universe, and everything?\""
    )

    @Argument(help: "A query for FastGPT") 
    public var query: String

    var client = Client.liveValue

    enum CodingKeys: CodingKey {
        case query
    }

    public init() {
        // Intentionally left empty
    }

    public mutating func run() async throws {
        do {
            let response = try await client.run(query: query)
            print(response)
        } catch {
            throw ValidationError(error.localizedDescription)
        }
    }
}

// MARK: - Processing

extension FastGPT {
    struct Client {
        var run: @Sendable (
            _ query: String
        ) async throws -> Response<Answer>

        @inlinable
        func run(
            query: String
        ) async throws -> Response<Answer> {
            try await run(query)
        }
    }
}

extension FastGPT.Client {
    static let liveValue = Self(
        run: { query in
            var request = try Endpoint.fastGPT.authorizedRequest
            request.httpBody = try JSONSerialization.data(withJSONObject: ["query": query])

            let (data, response) = try await URLSession.shared.data(for: request)

            do {
                return try JSONDecoder().decode(Response.self, from: data)
            } catch {
                #if DEBUG
                print(response)
                print(error)
                print("-----")
                #endif
                throw error
            }
        }
    )
}

// MARK: - Response

extension FastGPT {
    public struct Answer: CustomStringConvertible, Decodable {
        public struct Reference: CustomStringConvertible, Decodable {
            public var title: String
            public var snippet: String
            public var url: URL

            public var description: String {
                """
                - \(title)
                  \(url)

                  \(snippet.trimmingCharacters(in: .whitespaces))
                """
            }
        }

        public var output: String
        public var tokens: Int
        public var references: [Reference]

        public var description: String {
            """
            \(output)

            References:

            \(references.map(\.description).joined(separator: "\n\n"))
            """
        }
    }
}