import { CassandraProxyEndpoints, JunoEndpoints, MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import {
  allowedArcadiaEndpoints,
  allowedEmulatorEndpoints,
  allowedHostedExplorerEndpoints,
  allowedJunoOrigins,
  allowedMsalRedirectEndpoints,
  defaultAllowedAadEndpoints,
  defaultAllowedArmEndpoints,
  defaultAllowedBackendEndpoints,
  defaultAllowedCassandraProxyEndpoints,
  defaultAllowedGraphEndpoints,
  defaultAllowedMongoProxyEndpoints,
  validateEndpoint,
} from "Utils/EndpointUtils";

export enum Platform {
  Portal = "Portal",
  Hosted = "Hosted",
  Emulator = "Emulator",
  Fabric = "Fabric",
}

export interface ConfigContext {
  platform: Platform;
  allowedAadEndpoints: ReadonlyArray<string>;
  allowedGraphEndpoints: ReadonlyArray<string>;
  allowedArmEndpoints: ReadonlyArray<string>;
  allowedBackendEndpoints: ReadonlyArray<string>;
  allowedCassandraProxyEndpoints: ReadonlyArray<string>;
  allowedMongoProxyEndpoints: ReadonlyArray<string>;
  allowedParentFrameOrigins: ReadonlyArray<string>;
  SQL_DNS_ZONES: ReadonlyArray<string>;
  MONGO_DNS_ZONES: ReadonlyArray<string>;
  MONGO_COMPUTE_DNS_ZONES: ReadonlyArray<string>;
  CASSANDRA_DNS_ZONES: ReadonlyArray<string>;
  TABLE_DNS_ZONES: ReadonlyArray<string>;
  DOCUMENT_ENDPOINT_ZONES: ReadonlyArray<string>;
  GREMLIN_ENDPOINT_ZONES: ReadonlyArray<string>;
  gitSha?: string;
  proxyPath?: string;
  AAD_ENDPOINT: string;
  ARM_ENDPOINT: string;
  EMULATOR_ENDPOINT?: string;
  GRAPH_ENDPOINT: string;
  GRAPH_API_VERSION: string;
  // This is the endpoint to get offering Ids to be used to fetch prices. Refer to this doc: https://learn.microsoft.com/en-us/rest/api/marketplacecatalog/dataplane/skus/list?view=rest-marketplacecatalog-dataplane-2023-05-01-preview&tabs=HTTP
  CATALOG_ENDPOINT: string;
  CATALOG_API_VERSION: string;
  CATALOG_API_KEY: string;
  ARCADIA_ENDPOINT: string;
  ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
  PORTAL_BACKEND_ENDPOINT: string;
  MONGO_PROXY_ENDPOINT: string;
  CASSANDRA_PROXY_ENDPOINT: string;
  PROXY_PATH?: string;
  JUNO_ENDPOINT: string;
  isPhoenixEnabled: boolean;
  hostedExplorerURL: string;
  armAPIVersion?: string;
  msalRedirectURI?: string;
}

// Default configuration
let configContext: Readonly<ConfigContext> = {
  platform: Platform.Portal,
  allowedAadEndpoints: defaultAllowedAadEndpoints,
  allowedGraphEndpoints: defaultAllowedGraphEndpoints,
  allowedArmEndpoints: defaultAllowedArmEndpoints,
  allowedBackendEndpoints: defaultAllowedBackendEndpoints,
  allowedCassandraProxyEndpoints: defaultAllowedCassandraProxyEndpoints,
  allowedMongoProxyEndpoints: defaultAllowedMongoProxyEndpoints,
  allowedParentFrameOrigins: [
    `^https:\\/\\/cosmos\\.azure\\.(com|cn|us)$`,
    `^https:\\/\\/[\\.\\w]*portal\\.azure\\.(com|cn|us)$`,
    `^https:\\/\\/cdb-(ms|ff|mc)-prod-pbe\\.cosmos\\.azure\\.(com|us|cn)$`,
    `^https:\\/\\/[\\.\\w]*portal\\.microsoftazure\\.de$`,
    `^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`,
    `^https:\\/\\/[\\.\\w]*\\.ext\\.microsoftazure\\.de$`,
    `^https:\\/\\/cosmos-db-dataexplorer-germanycentral\\.azurewebsites\\.de$`,
    `^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
    `^https:\\/\\/.*\\.powerbi\\.com$`,
    `^https:\\/\\/dataexplorer-preview\\.portal\\.cosmos\\.azure\\.com$`,
    `^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.fr$`,
    `^https:\\/\\/portal\\.sovcloud-azure\\.fr$`,
    `^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.de$`,
    `^https:\\/\\/portal\\.sovcloud-azure\\.de$`,
    `^https:\\/\\/explorer\\.cosmos\\.sovcloud-api\\.sg$`,
    `^https:\\/\\/portal\\.sovcloud-azure\\.sg$`,
  ], // Webpack injects this at build time
  SQL_DNS_ZONES: [
    "documents.azure.com",
    "sql.cosmosdb.azure.com",
    "sql.cosmos.azure.com",
    "sqlx.cosmos.azure.com",
    "documents-staging.windows-ppe.net",
    "sql.cosmosdb.windows-ppe.net",
    "sql.cosmos.windows-ppe.net",
    "sqlx.cosmos.windows-ppe.net",
  ],
  MONGO_DNS_ZONES: ["documents.azure.com", "documents-staging.windows-ppe.net"],
  MONGO_COMPUTE_DNS_ZONES: ["mongo.cosmos.azure.com", "mongo.cosmos.windows-ppe.net"],
  CASSANDRA_DNS_ZONES: [
    "cassandra.cosmosdb.azure.com",
    "cassandra.cosmos.azure.com",
    "cassandra.cosmosdb.windows-ppe.net",
    "cassandra.cosmos.windows-ppe.net",
  ],
  TABLE_DNS_ZONES: [
    "table.cosmosdb.azure.com",
    "table.cosmos.azure.com",
    "table.cosmosdb.windows-ppe.net",
    "table.cosmos.windows-ppe.net",
  ],
  DOCUMENT_ENDPOINT_ZONES: ["documents.azure.com", "documents-staging.windows-ppe.net"],
  GREMLIN_ENDPOINT_ZONES: ["gremlin.cosmos.azure.com", "gremlin.cosmos.windows-ppe.net"],
  gitSha: process.env.GIT_SHA,
  hostedExplorerURL: "https://cosmos.azure.com/",
  AAD_ENDPOINT: "https://login.microsoftonline.com/",
  ARM_ENDPOINT: "https://management.azure.com/",
  GRAPH_ENDPOINT: "https://graph.microsoft.com",
  GRAPH_API_VERSION: "1.6",
  CATALOG_ENDPOINT: "https://catalogapi.azure.com/",
  CATALOG_API_VERSION: "2023-05-01-preview",
  CATALOG_API_KEY: "",
  ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
  ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
  JUNO_ENDPOINT: JunoEndpoints.Prod,
  PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
  MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
  CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
  isPhoenixEnabled: false,
};

export function resetConfigContext(): void {
  if (process.env.NODE_ENV !== "test") {
    throw new Error("resetConfigContext can only be called in a test environment");
  }
  configContext = {} as ConfigContext;
}

export function updateConfigContext(newContext: Partial<ConfigContext>): void {
  if (!newContext) {
    return;
  }

  if (newContext.allowedAadEndpoints) {
    Object.assign(configContext, { allowedAadEndpoints: newContext.allowedAadEndpoints });
  }
  if (newContext.allowedArmEndpoints) {
    Object.assign(configContext, { allowedArmEndpoints: newContext.allowedArmEndpoints });
  }
  if (newContext.allowedGraphEndpoints) {
    Object.assign(configContext, { allowedGraphEndpoints: newContext.allowedGraphEndpoints });
  }
  if (newContext.allowedBackendEndpoints) {
    Object.assign(configContext, { allowedBackendEndpoints: newContext.allowedBackendEndpoints });
  }
  if (newContext.allowedMongoProxyEndpoints) {
    Object.assign(configContext, { allowedMongoProxyEndpoints: newContext.allowedMongoProxyEndpoints });
  }
  if (newContext.allowedCassandraProxyEndpoints) {
    Object.assign(configContext, { allowedCassandraProxyEndpoints: newContext.allowedCassandraProxyEndpoints });
  }

  if (!validateEndpoint(newContext.AAD_ENDPOINT, configContext.allowedAadEndpoints)) {
    delete newContext.AAD_ENDPOINT;
  }

  if (!validateEndpoint(newContext.ARM_ENDPOINT, configContext.allowedArmEndpoints)) {
    delete newContext.ARM_ENDPOINT;
  }

  if (!validateEndpoint(newContext.EMULATOR_ENDPOINT, allowedEmulatorEndpoints)) {
    delete newContext.EMULATOR_ENDPOINT;
  }

  if (!validateEndpoint(newContext.GRAPH_ENDPOINT, configContext.allowedGraphEndpoints)) {
    delete newContext.GRAPH_ENDPOINT;
  }

  if (!validateEndpoint(newContext.ARCADIA_ENDPOINT, allowedArcadiaEndpoints)) {
    delete newContext.ARCADIA_ENDPOINT;
  }

  if (!validateEndpoint(newContext.PORTAL_BACKEND_ENDPOINT, configContext.allowedBackendEndpoints)) {
    delete newContext.PORTAL_BACKEND_ENDPOINT;
  }

  if (!validateEndpoint(newContext.MONGO_PROXY_ENDPOINT, configContext.allowedMongoProxyEndpoints)) {
    delete newContext.MONGO_PROXY_ENDPOINT;
  }

  if (!validateEndpoint(newContext.CASSANDRA_PROXY_ENDPOINT, configContext.allowedCassandraProxyEndpoints)) {
    delete newContext.CASSANDRA_PROXY_ENDPOINT;
  }

  if (!validateEndpoint(newContext.JUNO_ENDPOINT, allowedJunoOrigins)) {
    delete newContext.JUNO_ENDPOINT;
  }

  if (!validateEndpoint(newContext.hostedExplorerURL, allowedHostedExplorerEndpoints)) {
    delete newContext.hostedExplorerURL;
  }

  if (!validateEndpoint(newContext.msalRedirectURI, allowedMsalRedirectEndpoints)) {
    delete newContext.msalRedirectURI;
  }

  Object.assign(configContext, newContext);
}

// Injected for local development. These will be removed in the production bundle by webpack
if (process.env.NODE_ENV === "development") {
  updateConfigContext({
    PROXY_PATH: "/proxy",
    EMULATOR_ENDPOINT: "https://localhost:8081",
    PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
    MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
    CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac,
  });
}

export async function initializeConfiguration(): Promise<ConfigContext> {
  try {
    const response = await fetch("./config.json", {
      headers: {
        "If-None-Match": "", // disable client side cache
      },
    });
    if (response.status === 200) {
      try {
        const { ...externalConfig } = await response.json();
        updateConfigContext(externalConfig);
      } catch (error) {
        console.error("Unable to parse json in config file");
        console.error(error);
      }
    }
    // Allow override of platform value with URL query parameter
    const params = new URLSearchParams(window.location.search);
    if (params.has("armAPIVersion")) {
      const armAPIVersion = params.get("armAPIVersion") || "";
      updateConfigContext({ armAPIVersion });
    }
    if (params.has("armEndpoint")) {
      const ARM_ENDPOINT = params.get("armEndpoint") || "";
      updateConfigContext({ ARM_ENDPOINT });
    }
    if (params.has("aadEndpoint")) {
      const AAD_ENDPOINT = params.get("aadEndpoint") || "";
      updateConfigContext({ AAD_ENDPOINT });
    }
    if (params.has("platform")) {
      const platform = params.get("platform");
      switch (platform) {
        default:
          console.error(`Invalid platform query parameter: ${platform}`);
          break;
        case Platform.Portal:
        case Platform.Fabric:
        case Platform.Hosted:
        case Platform.Emulator:
          updateConfigContext({ platform });
      }
    }
  } catch (error) {
    console.error("No configuration file found using defaults");
  }
  return configContext;
}

export { configContext };
