#include "chat/chat_fixture_tooling.h"

#include <algorithm>
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>

namespace fs = std::filesystem;

namespace {

using dragonx::chat::HushChatCompatibilityFixtureImportCandidate;
using dragonx::chat::HushChatCompatibilityFixtureKind;

std::string baseName(const std::string& path)
{
    return fs::path(path).filename().string();
}

std::vector<std::string> jsonFilesInDirectory(const fs::path& directory)
{
    std::vector<std::string> paths;
    if (!fs::exists(directory) || !fs::is_directory(directory)) return paths;

    for (const auto& entry : fs::directory_iterator(directory)) {
        if (!entry.is_regular_file()) continue;
        if (entry.path().extension() != ".json") continue;
        paths.push_back(entry.path().string());
    }
    std::sort(paths.begin(), paths.end());
    return paths;
}

HushChatCompatibilityFixtureKind expectedKindFromPath(const std::string& path)
{
    const std::string name = baseName(path);
    if (name.find("outgoing") != std::string::npos) return HushChatCompatibilityFixtureKind::OutgoingMemo;
    if (name.find("seed") != std::string::npos) return HushChatCompatibilityFixtureKind::SeedPublicKeyProjection;
    if (name.find("corrupted") != std::string::npos) return HushChatCompatibilityFixtureKind::CorruptedAuthFailure;
    if (name.find("cont") != std::string::npos) return HushChatCompatibilityFixtureKind::ContactExclusion;
    return HushChatCompatibilityFixtureKind::IncomingMemo;
}

void printUsage(const char* program)
{
    std::cerr << "Usage: " << program
              << " [--allow-pending] [--replacement-dry-run] [--validate-capture-manifest]"
              << " <fixture-file-or-directory>...\n";
}

const char* boolText(bool value)
{
    return value ? "1" : "0";
}

void printReplacementDryRun(const dragonx::chat::HushChatCompatibilityFixtureReplacementDryRunResult& dryRun)
{
    std::cout << "HushChat fixture replacement dry run: "
              << (dryRun.ok ? "Ready" : "Refused") << "\n";
    std::cout << "error=" << dryRun.error_name
              << " dry_run_only=" << boolText(dryRun.dry_run_only)
              << " redacted=" << boolText(dryRun.redacted_report)
              << " would_replace=" << boolText(dryRun.would_replace)
              << " replacement_refused=" << boolText(dryRun.replacement_refused) << "\n";
    std::cout << "required=" << dryRun.required_count
              << " supplied=" << dryRun.supplied_count
              << " missing=" << dryRun.missing_count
              << " verified=" << dryRun.verified_count
              << " seed_projected=" << dryRun.seed_projection_verified_count
              << " future_auth_required=" << dryRun.future_auth_failure_required_count
              << " auth_structural_ready=" << dryRun.auth_failure_structural_ready_count
              << " cont_excluded=" << dryRun.excluded_count
              << " pending=" << dryRun.pending_count
              << " rejected=" << dryRun.rejected_count << "\n";

    for (const auto& item : dryRun.report_items) {
        std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(item.expected_kind)
                  << " decision=" << (item.replacement_eligible ? "ready" : "refused")
                  << " error=" << item.error_name
                  << " supplied=" << boolText(item.supplied)
                  << " pending=" << boolText(item.pending)
                  << " seed_projected=" << boolText(item.seed_projection_verified)
                  << " future_auth_required=" << boolText(item.future_auth_failure_required)
                  << " auth_structural_ready=" << boolText(item.structurally_ready_for_future_auth_check)
                  << " cont_excluded=" << boolText(item.cont_excluded)
                  << " decrypted=" << boolText(item.decrypted)
                  << " authenticated=" << boolText(item.authenticated);
        if (item.supplied) std::cout << " file=" << baseName(item.path);
        std::cout << "\n";
    }
}

std::string captureManifestPathFromInput(const std::string& input)
{
    fs::path path(input);
    if (fs::exists(path) && fs::is_directory(path)) return (path / "capture-manifest.json").string();
    return input;
}

void printCaptureManifestValidation(const dragonx::chat::HushChatCaptureManifestValidationResult& validation)
{
    std::cout << "HushChat capture manifest: " << (validation.ok ? "Ready" : "Refused") << "\n";
    std::cout << "error=" << validation.error_name
              << " redacted=" << boolText(validation.redacted_report)
              << " provenance_only=" << boolText(validation.validates_provenance_only)
              << " no_sensitive_material=" << boolText(validation.no_sensitive_material_declared)
              << " dry_run_instruction=" << boolText(validation.has_dry_run_command);
    if (!validation.manifest_path.empty()) std::cout << " manifest=" << baseName(validation.manifest_path);
    if (!validation.fixture_directory.empty()) std::cout << " fixture_dir=" << baseName(validation.fixture_directory);
    std::cout << "\n";
    std::cout << "required=" << validation.required_count
              << " declared=" << validation.declared_count
              << " missing=" << validation.missing_count
              << " duplicate=" << validation.duplicate_count
              << " handling_flags=" << validation.handling_flag_count
              << " prohibited=" << validation.prohibited_field_count << "\n";

    for (const auto& category : validation.categories) {
        std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(category.kind)
                  << " declared=" << boolText(category.declared);
        if (!category.staged_filename.empty()) std::cout << " file=" << baseName(category.staged_filename);
        std::cout << "\n";
    }
}

} // namespace

int main(int argc, char** argv)
{
    bool allowPending = false;
    bool replacementDryRun = false;
    bool validateCaptureManifest = false;
    std::vector<std::string> inputPaths;

    for (int index = 1; index < argc; ++index) {
        const std::string arg = argv[index];
        if (arg == "--allow-pending") {
            allowPending = true;
        } else if (arg == "--replacement-dry-run") {
            replacementDryRun = true;
        } else if (arg == "--validate-capture-manifest") {
            validateCaptureManifest = true;
        } else if (arg == "--help" || arg == "-h") {
            printUsage(argv[0]);
            return 0;
        } else {
            inputPaths.push_back(arg);
        }
    }

    if (inputPaths.empty()) {
        printUsage(argv[0]);
        return 2;
    }

    if (validateCaptureManifest && (allowPending || replacementDryRun)) {
        std::cerr << "Capture manifest validation is separate from fixture and replacement checks.\n";
        return 2;
    }
    if (replacementDryRun && allowPending) {
        std::cerr << "Replacement dry run is strict; remove --allow-pending.\n";
        return 2;
    }

    if (validateCaptureManifest) {
        bool allValid = true;
        for (const auto& input : inputPaths) {
            const auto validation = dragonx::chat::loadHushChatCaptureManifestFile(
                captureManifestPathFromInput(input),
                true);
            printCaptureManifestValidation(validation);
            if (!validation.ok) allValid = false;
        }
        return allValid ? 0 : 1;
    }

    std::vector<std::string> paths;
    for (const auto& input : inputPaths) {
        fs::path path(input);
        if (fs::exists(path) && fs::is_directory(path)) {
            auto directoryFiles = jsonFilesInDirectory(path);
            paths.insert(paths.end(), directoryFiles.begin(), directoryFiles.end());
        } else {
            paths.push_back(input);
        }
    }

    std::vector<HushChatCompatibilityFixtureImportCandidate> candidates;
    candidates.reserve(paths.size());
    for (const auto& path : paths) {
        candidates.push_back(HushChatCompatibilityFixtureImportCandidate{expectedKindFromPath(path), path});
    }

    if (replacementDryRun) {
        const auto dryRun = dragonx::chat::inspectHushChatCompatibilityFixtureReplacementDryRun(candidates, true);
        printReplacementDryRun(dryRun);
        return dryRun.ok ? 0 : 1;
    }

    const auto checklist = dragonx::chat::inspectHushChatCompatibilityFixtureImportChecklist(candidates, true);
    std::cout << "HushChat fixture import checklist: " << checklist.error_name << "\n";
    std::cout << "required=" << checklist.required_count
              << " supplied=" << checklist.supplied_count
              << " missing=" << checklist.missing_count
              << " verified=" << checklist.verified_count
              << " seed_projected=" << checklist.seed_projection_verified_count
              << " future_auth_required=" << checklist.future_auth_failure_required_count
              << " auth_structural_ready=" << checklist.auth_failure_structural_ready_count
              << " cont_excluded=" << checklist.excluded_count
              << " pending=" << checklist.pending_count
              << " rejected=" << checklist.rejected_count << "\n";

    for (const auto& item : checklist.items) {
        std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(item.expected_kind)
                  << " " << item.error_name;
        if (item.future_auth_failure_required) std::cout << " FutureAuthFailureRequired";
        if (item.structurally_ready_for_future_auth_check) std::cout << " StructurallyReadyForFutureAuthCheck";
        if (item.supplied) std::cout << " " << baseName(item.path);
        std::cout << "\n";
    }

    if (checklist.replacement_ready) return 0;
    if (allowPending && checklist.supplied_count == checklist.required_count &&
        checklist.missing_count == 0 && checklist.pending_count > 0 && checklist.rejected_count == 0) return 0;
    return 1;
}