import type { RemoteDBSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";

type EndpointProjection = readonly [kind: "url" | "invalid-url", value: string];

/**
 * Compare the effective endpoint rather than inconsequential URI spelling.
 * Fragments are not sent, query order is immaterial, and redundant trailing
 * slashes do not bind a different adapter. Invalid input is retained verbatim
 * and tagged so comparison remains deterministic and fails closed.
 */
function projectEndpoint(value: string): EndpointProjection {
    try {
        const endpoint = new URL(value);
        endpoint.hash = "";
        endpoint.searchParams.sort();
        while (endpoint.pathname.length > 1 && endpoint.pathname.endsWith("/")) {
            endpoint.pathname = endpoint.pathname.slice(0, -1);
        }
        return ["url", endpoint.toString()];
    } catch {
        return ["invalid-url", value];
    }
}

/**
 * Mirror the effective custom-header parser: trim each first name/value pair,
 * ignore incomplete lines, and let the last duplicate name win. Sorting the
 * resulting entries prevents line order alone from replacing a Replicator.
 */
function projectHeaders(value: string): readonly (readonly [name: string, value: string])[] {
    const headers = new Map<string, string>();
    for (const line of value.split("\n")) {
        const [name, headerValue] = line.split(":", 2).map((part) => part.trim());
        if (name && headerValue) {
            headers.set(name, headerValue);
        }
    }
    return [...headers.entries()].sort(([leftName, leftValue], [rightName, rightValue]) => {
        const nameOrder = leftName.localeCompare(rightName);
        return nameOrder || leftValue.localeCompare(rightValue);
    });
}

function projectRemoteSecurity(settings: RemoteDBSettings) {
    return settings.encrypt
        ? ([
              "encrypted",
              settings.passphrase,
              settings.useDynamicIterationCount,
              settings.E2EEAlgorithm,
              settings.permitEmptyPassphrase,
          ] as const)
        : (["plain"] as const);
}

/**
 * Project the effective CouchDB connection settings to a private comparison identity.
 * The returned value can contain credentials and must not be logged, persisted, or displayed.
 */
export function getCouchDBReplicatorConfigurationIdentity(settings: RemoteDBSettings): string {
    const authentication = settings.useJWT
        ? ([
              "jwt",
              settings.jwtAlgorithm,
              settings.jwtKey,
              settings.jwtKid,
              settings.jwtSub,
              settings.jwtExpDuration,
          ] as const)
        : (["basic", settings.couchDB_USER, settings.couchDB_PASSWORD] as const);
    return JSON.stringify([
        "couchdb",
        projectEndpoint(settings.couchDB_URI),
        settings.couchDB_DBNAME,
        authentication,
        projectHeaders(settings.couchDB_CustomHeaders),
        settings.useRequestAPI,
        settings.disableRequestURI,
        projectRemoteSecurity(settings),
        settings.enableCompression,
    ]);
}

/**
 * Project the effective Object Storage connection settings to a private comparison identity.
 * The returned value can contain credentials and must not be logged, persisted, or displayed.
 */
export function getObjectStorageReplicatorConfigurationIdentity(settings: RemoteDBSettings): string {
    return JSON.stringify([
        "s3",
        projectEndpoint(settings.endpoint),
        settings.bucket,
        settings.bucketPrefix,
        settings.region,
        settings.accessKey,
        settings.secretKey,
        settings.forcePathStyle,
        settings.useCustomRequestHandler,
        projectHeaders(settings.bucketCustomHeaders),
        projectRemoteSecurity(settings),
    ]);
}
