#include "wallet/lite_backend_artifact_contract.h"

#include <algorithm>
#include <cctype>
#include <sstream>
#include <utility>

namespace dragonx::wallet {
namespace {

constexpr const char* kSupportedAbiVersion = "sdxl-c-v1";

using Issue = LiteBackendArtifactContractIssue;
using Status = LiteBackendArtifactContractStatus;

void addIssue(LiteBackendArtifactContractResult& result, Issue issue, std::string message)
{
    result.issues.push_back({issue, std::move(message)});
    if (result.error.empty()) result.error = result.issues.back().message;
}

LiteBackendArtifactContractResult stoppedResult(
    LiteBackendArtifactContractResult result,
    Status status,
    Issue issue,
    std::string message)
{
    result.status = status;
    addIssue(result, issue, std::move(message));
    return result;
}

std::string trimCopy(const std::string& value)
{
    auto begin = value.begin();
    while (begin != value.end() && std::isspace(static_cast<unsigned char>(*begin))) ++begin;

    auto end = value.end();
    while (end != begin && std::isspace(static_cast<unsigned char>(*(end - 1)))) --end;

    return std::string(begin, end);
}

bool containsSymbol(const std::vector<std::string>& exportedSymbols, const char* abiName)
{
    return std::find(exportedSymbols.begin(), exportedSymbols.end(), abiName) != exportedSymbols.end();
}

bool importedArtifactKindLinkable(LiteBackendArtifactKind kind)
{
    return kind == LiteBackendArtifactKind::StaticLibrary || kind == LiteBackendArtifactKind::SharedLibrary;
}

bool signatureTrustMetadataProvided(const LiteBackendArtifactSignatureVerificationMetadata& signature)
{
    return !trimCopy(signature.keyFingerprint).empty() || !trimCopy(signature.certificateIdentity).empty();
}

LiteBackendArtifactContractResult validateSignatureMetadata(
    LiteBackendArtifactContractResult result,
    const LiteBackendArtifactContractInput& input,
    LiteBackendArtifactContractOptions options)
{
    const auto& signature = input.signatureVerification;
    result.signatureRequiredForRelease = signature.requiredForRelease;
    const bool shouldValidate = signature.requiredForRelease ||
        (options.validateOptionalSignatureMetadata && signature.metadataProvided);

    if (!shouldValidate) {
        result.signatureMetadataAccepted = true;
        result.signatureVerificationForwarded = signature.metadataProvided;
        result.signatureVerified = signature.verified;
        return result;
    }

    if (options.requireSignatureMetadataWhenRequired && !signature.policyDefined) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignaturePolicyMissing,
                             "lite backend artifact signature policy is not defined");
    }
    if (!signature.metadataProvided) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureMetadataMissing,
                             "lite backend artifact signature metadata is required but missing");
    }
    if (trimCopy(signature.signatureFormat).empty() ||
        trimCopy(signature.signaturePath).empty() ||
        trimCopy(signature.verificationTool).empty() ||
        trimCopy(signature.verifiedArtifactSha256).empty()) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureMetadataIncomplete,
                             "lite backend artifact signature metadata is incomplete");
    }
    if (!signatureTrustMetadataProvided(signature)) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureTrustMetadataMissing,
                             "lite backend artifact signature metadata is missing a reviewed trust identity");
    }
    if (!signature.verificationPerformed) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureVerificationMissing,
                             "lite backend artifact signature verification has not been performed");
    }
    if (!signature.verified) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureVerificationFailed,
                             "lite backend artifact signature verification did not pass");
    }
    if (!trimCopy(input.artifactSha256).empty() && signature.verifiedArtifactSha256 != input.artifactSha256) {
        return stoppedResult(std::move(result), Status::WaitingForSignatureMetadata, Issue::SignatureVerifiedArtifactShaMismatch,
                             "lite backend artifact signature metadata does not match the artifact SHA-256");
    }

    result.signatureMetadataAccepted = true;
    result.signatureVerificationForwarded = true;
    result.signatureVerified = true;
    return result;
}

LiteBackendArtifactContractResult rejectRuntimeActions(
    LiteBackendArtifactContractResult result,
    const LiteBackendArtifactContractInput& input,
    LiteBackendArtifactContractOptions options)
{
    if (!options.rejectRuntimeActions) return result;
    if (input.artifactMutationRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::ArtifactMutationRequested,
                             "lite backend artifact contract cannot mutate artifacts");
    }
    if (input.dynamicLibraryLoadRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::DynamicLibraryLoadRequested,
                             "lite backend artifact contract cannot load dynamic libraries");
    }
    if (input.dynamicLibraryUnloadRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::DynamicLibraryUnloadRequested,
                             "lite backend artifact contract cannot unload dynamic libraries");
    }
    if (input.symbolResolutionRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::SymbolResolutionRequested,
                             "lite backend artifact contract cannot resolve runtime symbols");
    }
    if (input.sdxlApiRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::SdxlApiRequested,
                             "lite backend artifact contract cannot call SDXL APIs");
    }
    if (input.bridgeCallRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::BridgeCallRequested,
                             "lite backend artifact contract cannot call the bridge");
    }
    if (input.serverConnectivityCheckRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::ServerConnectivityCheckRequested,
                             "lite backend artifact contract cannot check server connectivity");
    }
    if (input.walletLifecycleRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::WalletLifecycleRequested,
                             "lite backend artifact contract cannot execute wallet lifecycle flows");
    }
    if (input.syncRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::SyncRequested,
                             "lite backend artifact contract cannot start sync");
    }
    if (input.syncStatusPollingRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::SyncStatusPollingRequested,
                             "lite backend artifact contract cannot poll syncstatus");
    }
    if (input.workerQueueRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::WorkerQueueRequested,
                             "lite backend artifact contract cannot enqueue workers");
    }
    if (input.walletStateMutationRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::WalletStateMutationRequested,
                             "lite backend artifact contract cannot mutate WalletState");
    }
    if (input.walletPersistenceRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::WalletPersistenceRequested,
                             "lite backend artifact contract cannot persist wallet files");
    }
    if (input.uploadRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::UploadRequested,
                             "lite backend artifact contract cannot upload artifacts");
    }
    if (input.signingRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::SigningRequested,
                             "lite backend artifact contract cannot sign artifacts");
    }
    if (input.publicationRequested) {
        return stoppedResult(std::move(result), Status::RuntimeActionDisabled, Issue::PublicationRequested,
                             "lite backend artifact contract cannot publish artifacts");
    }
    return result;
}

} // namespace

const char* liteBackendArtifactContractSupportedAbiVersion()
{
    return kSupportedAbiVersion;
}

const char* liteBackendArtifactContractLinkModeName(LiteBackendArtifactContractLinkMode linkMode)
{
    switch (linkMode) {
        case LiteBackendArtifactContractLinkMode::ImportedLibrary: return "ImportedLibrary";
        case LiteBackendArtifactContractLinkMode::RuntimeDynamicLibrary: return "RuntimeDynamicLibrary";
        case LiteBackendArtifactContractLinkMode::ExternalExecutable: return "ExternalExecutable";
    }
    return "Unknown";
}

const char* liteBackendArtifactContractStatusName(LiteBackendArtifactContractStatus status)
{
    switch (status) {
        case LiteBackendArtifactContractStatus::ReadyForResolver: return "ReadyForResolver";
        case LiteBackendArtifactContractStatus::WaitingForContractOwner: return "WaitingForContractOwner";
        case LiteBackendArtifactContractStatus::WaitingForReadOnlyGate: return "WaitingForReadOnlyGate";
        case LiteBackendArtifactContractStatus::WaitingForLinkMode: return "WaitingForLinkMode";
        case LiteBackendArtifactContractStatus::WaitingForArtifactPath: return "WaitingForArtifactPath";
        case LiteBackendArtifactContractStatus::WaitingForArtifactKind: return "WaitingForArtifactKind";
        case LiteBackendArtifactContractStatus::WaitingForAbiVersion: return "WaitingForAbiVersion";
        case LiteBackendArtifactContractStatus::WaitingForSymbolInventory: return "WaitingForSymbolInventory";
        case LiteBackendArtifactContractStatus::WaitingForRequiredSymbols: return "WaitingForRequiredSymbols";
        case LiteBackendArtifactContractStatus::WaitingForSignatureMetadata: return "WaitingForSignatureMetadata";
        case LiteBackendArtifactContractStatus::RuntimeActionDisabled: return "RuntimeActionDisabled";
    }
    return "Unknown";
}

const char* liteBackendArtifactContractIssueName(LiteBackendArtifactContractIssue issue)
{
    switch (issue) {
#define DRAGONX_CASE(value) case LiteBackendArtifactContractIssue::value: return #value
        DRAGONX_CASE(ContractOwnerMissing);
        DRAGONX_CASE(ReadOnlyGateMissing);
        DRAGONX_CASE(RuntimeDynamicLinkDeferred);
        DRAGONX_CASE(ExternalExecutableDeferred);
        DRAGONX_CASE(ArtifactPathMissing);
        DRAGONX_CASE(ArtifactKindNotLinkable);
        DRAGONX_CASE(AbiVersionMissing);
        DRAGONX_CASE(AbiVersionUnsupported);
        DRAGONX_CASE(SymbolInventoryOwnerMissing);
        DRAGONX_CASE(SymbolInventoryMissing);
        DRAGONX_CASE(RequiredSymbolMissing);
        DRAGONX_CASE(SignaturePolicyMissing);
        DRAGONX_CASE(SignatureMetadataMissing);
        DRAGONX_CASE(SignatureMetadataIncomplete);
        DRAGONX_CASE(SignatureVerificationMissing);
        DRAGONX_CASE(SignatureVerificationFailed);
        DRAGONX_CASE(SignatureVerifiedArtifactShaMismatch);
        DRAGONX_CASE(SignatureTrustMetadataMissing);
        DRAGONX_CASE(ArtifactMutationRequested);
        DRAGONX_CASE(DynamicLibraryLoadRequested);
        DRAGONX_CASE(DynamicLibraryUnloadRequested);
        DRAGONX_CASE(SymbolResolutionRequested);
        DRAGONX_CASE(SdxlApiRequested);
        DRAGONX_CASE(BridgeCallRequested);
        DRAGONX_CASE(ServerConnectivityCheckRequested);
        DRAGONX_CASE(WalletLifecycleRequested);
        DRAGONX_CASE(SyncRequested);
        DRAGONX_CASE(SyncStatusPollingRequested);
        DRAGONX_CASE(WorkerQueueRequested);
        DRAGONX_CASE(WalletStateMutationRequested);
        DRAGONX_CASE(WalletPersistenceRequested);
        DRAGONX_CASE(UploadRequested);
        DRAGONX_CASE(SigningRequested);
        DRAGONX_CASE(PublicationRequested);
#undef DRAGONX_CASE
    }
    return "Unknown";
}

std::vector<LiteBackendArtifactContractSymbol> liteBackendArtifactContractRequiredSymbols()
{
    return {
        {"walletExists", "litelib_wallet_exists", false, false, true, false, false, false},
        {"initializeNew", "litelib_initialize_new", true, false, true, false, false, false},
        {"initializeNewFromPhrase", "litelib_initialize_new_from_phrase", true, false, true, false, false, false},
        {"initializeExisting", "litelib_initialize_existing", true, false, true, false, false, false},
        {"execute", "litelib_execute", true, false, false, true, false, false},
        {"freeString", "litelib_rust_free_string", false, true, true, true, false, false},
        {"checkServerOnline", "litelib_check_server_online", false, false, false, false, true, false},
        {"shutdown", "litelib_shutdown", false, false, true, true, true, true},
    };
}

LiteWalletSdxlArtifactSymbolsInput liteBackendArtifactSymbolsFromExportedNames(
    const std::vector<std::string>& exportedSymbols)
{
    LiteWalletSdxlArtifactSymbolsInput symbols;
    symbols.walletExists = containsSymbol(exportedSymbols, "litelib_wallet_exists");
    symbols.initializeNew = containsSymbol(exportedSymbols, "litelib_initialize_new");
    symbols.initializeNewFromPhrase = containsSymbol(exportedSymbols, "litelib_initialize_new_from_phrase");
    symbols.initializeExisting = containsSymbol(exportedSymbols, "litelib_initialize_existing");
    symbols.execute = containsSymbol(exportedSymbols, "litelib_execute");
    symbols.freeString = containsSymbol(exportedSymbols, "litelib_rust_free_string");
    symbols.checkServerOnline = containsSymbol(exportedSymbols, "litelib_check_server_online");
    symbols.shutdown = containsSymbol(exportedSymbols, "litelib_shutdown");
    return symbols;
}

LiteBackendArtifactContractResult evaluateLiteBackendArtifactContract(
    const LiteBackendArtifactContractInput& input,
    LiteBackendArtifactContractOptions options)
{
    LiteBackendArtifactContractResult result;
    result.requiredSymbolCount = liteBackendArtifactContractRequiredSymbols().size();
    result.exportedSymbolCount = input.exportedSymbols.size();

    result = rejectRuntimeActions(std::move(result), input, options);
    if (!result.issues.empty()) return result;

    if (options.requireContractOwner && !input.contractOwnerReady) {
        return stoppedResult(std::move(result), Status::WaitingForContractOwner, Issue::ContractOwnerMissing,
                             "lite backend artifact contract owner is not ready");
    }
    result.contractOwnerAccepted = true;

    if (options.requireReadOnlyGate && !input.readOnlyGateReady) {
        return stoppedResult(std::move(result), Status::WaitingForReadOnlyGate, Issue::ReadOnlyGateMissing,
                             "lite backend artifact contract read-only gate is not ready");
    }
    result.readOnlyGateAccepted = true;

    if (options.requireImportedLibraryLinkMode && input.linkMode == LiteBackendArtifactContractLinkMode::RuntimeDynamicLibrary) {
        return stoppedResult(std::move(result), Status::WaitingForLinkMode, Issue::RuntimeDynamicLinkDeferred,
                             "runtime dynamic loading is deferred to the bridge runtime owner phase");
    }
    if (options.requireImportedLibraryLinkMode && input.linkMode == LiteBackendArtifactContractLinkMode::ExternalExecutable) {
        return stoppedResult(std::move(result), Status::WaitingForLinkMode, Issue::ExternalExecutableDeferred,
                             "external executable bridge mode is not the Phase 1 production link contract");
    }
    result.importedLinkModeAccepted = input.linkMode == LiteBackendArtifactContractLinkMode::ImportedLibrary;

    if (trimCopy(input.artifactPath).empty()) {
        return stoppedResult(std::move(result), Status::WaitingForArtifactPath, Issue::ArtifactPathMissing,
                             "lite backend artifact contract requires an artifact path");
    }
    result.artifactPathAccepted = true;

    if (!importedArtifactKindLinkable(input.artifactKind)) {
        return stoppedResult(std::move(result), Status::WaitingForArtifactKind, Issue::ArtifactKindNotLinkable,
                             "Phase 1 imported link mode requires a static or shared library artifact");
    }
    result.artifactKindAccepted = true;

    if (options.requireAbiVersion && trimCopy(input.abiVersion).empty()) {
        return stoppedResult(std::move(result), Status::WaitingForAbiVersion, Issue::AbiVersionMissing,
                             "lite backend artifact ABI version is missing");
    }
    if (options.requireAbiVersion && input.abiVersion != kSupportedAbiVersion) {
        return stoppedResult(std::move(result), Status::WaitingForAbiVersion, Issue::AbiVersionUnsupported,
                             "lite backend artifact ABI version is unsupported");
    }
    result.abiVersionAccepted = true;

    result = validateSignatureMetadata(std::move(result), input, options);
    if (!result.issues.empty()) return result;

    if (options.requireSymbolInventory && !input.symbolInventoryOwnerReady) {
        return stoppedResult(std::move(result), Status::WaitingForSymbolInventory, Issue::SymbolInventoryOwnerMissing,
                             "lite backend artifact symbol inventory owner is not ready");
    }
    if (options.requireSymbolInventory && input.exportedSymbols.empty()) {
        return stoppedResult(std::move(result), Status::WaitingForSymbolInventory, Issue::SymbolInventoryMissing,
                             "lite backend artifact exported symbol inventory is missing");
    }
    result.symbolInventoryAccepted = true;

    result.symbols = liteBackendArtifactSymbolsFromExportedNames(input.exportedSymbols);
    for (const auto& symbol : liteBackendArtifactContractRequiredSymbols()) {
        if (!containsSymbol(input.exportedSymbols, symbol.abiName.c_str())) {
            result.missingSymbols.push_back(symbol.abiName);
        }
    }
    if (options.requireAllRequiredSymbols && !result.missingSymbols.empty()) {
        return stoppedResult(std::move(result), Status::WaitingForRequiredSymbols, Issue::RequiredSymbolMissing,
                             "lite backend artifact is missing required C ABI symbols");
    }
    result.requiredSymbolsAccepted = true;

    result.resolverCandidate.configured = true;
    result.resolverCandidate.artifactPath = input.artifactPath;
    result.resolverCandidate.platform = input.platform;
    result.resolverCandidate.kind = input.artifactKind;
    result.resolverCandidate.versionLabel = input.abiVersion;
    result.resolverCandidate.provenance = input.provenance;
    result.resolverCandidate.signatureVerification = input.signatureVerification;
    result.resolverCandidate.sdxlCompatible = input.sdxlCompatible;
    result.resolverCandidate.symbols = result.symbols;
    result.resolverCandidate.executableMetadataRequired = false;

    result.resolverInput.resolverOwnerReady = true;
    result.resolverInput.readOnlyGateReady = true;
    result.resolverInput.projectRoot = input.projectRoot;
    result.resolverInput.expectedPlatform = input.platform;
    result.resolverInput.candidates.push_back(result.resolverCandidate);
    result.resolverInputProduced = true;
    result.provenanceForwarded = input.provenance.ownerReady && input.provenance.metadataProvided;
    result.signatureVerificationForwarded = input.signatureVerification.metadataProvided;
    result.sdxlCompatibilityForwarded = input.sdxlCompatible;
    result.dynamicLibraryLoadAllowed = false;
    result.symbolResolutionAllowed = false;
    result.runtimeActivationAllowed = false;
    result.ok = true;
    result.status = Status::ReadyForResolver;

    std::ostringstream summary;
    summary << "lite_backend_artifact_contract=ready"
            << ";abi=" << input.abiVersion
            << ";link_mode=" << liteBackendArtifactContractLinkModeName(input.linkMode)
            << ";kind=" << liteBackendArtifactKindName(input.artifactKind)
            << ";symbols=" << result.requiredSymbolCount
            << ";signature_required=" << (result.signatureRequiredForRelease ? "true" : "false")
            << ";signature_verified=" << (result.signatureVerified ? "true" : "false")
            << ";dynamic_loading=false;runtime_activation=false";
    result.summary = summary.str();
    return result;
}

LiteBackendArtifactResolverInput liteBackendArtifactResolverInputFromContractResult(
    const LiteBackendArtifactContractResult& result)
{
    return result.resolverInput;
}

} // namespace dragonx::wallet