import { sanitizeUrl } from "@braintree/sanitize-url";
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import {
  protocolIntegrationPicked,
  PROTOCOL_INTEGRATIONS,
} from "../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../common/navigate";
import {
  createSearchParam,
  extractSearchParamsObject,
} from "../../common/url/search-params";
import { domainToName } from "../../data/integration";
import "../../layouts/hass-error-screen";
import type { HomeAssistant, Route } from "../../types";
import { documentationUrl } from "../../util/documentation-url";

// When a user presses "m", the user is redirected to the first redirect
// for which holds true currentPath.startsWith(redirect.redirect)
// That's why redirects should be sorted with more specific ones first
// Or else pressing "M" will link to the higher level page.

export const getMyRedirects = (): Redirects => ({
  application_credentials: {
    redirect: "/config/application_credentials",
  },
  tools_assist: {
    redirect: "/config/tools/assist",
  },
  tools_debug: {
    redirect: "/config/tools/debug",
  },
  tools_states: {
    redirect: "/config/tools/state",
  },
  tools_actions: {
    redirect: "/config/tools/action",
  },
  tools_perform_action: {
    redirect: "/config/tools/action",
    params: {
      service: "string",
    },
  },
  tools_template: {
    redirect: "/config/tools/template",
  },
  tools_events: {
    redirect: "/config/tools/event",
  },
  tools_statistics: {
    redirect: "/config/tools/statistics",
  },
  tools_yaml: {
    redirect: "/config/tools/yaml",
  },
  developer_assist: {
    redirect: "/config/tools/assist",
  },
  developer_debug: {
    redirect: "/config/tools/debug",
  },
  developer_states: {
    redirect: "/config/tools/state",
  },
  developer_services: {
    redirect: "/config/tools/action",
  },
  developer_call_service: {
    redirect: "/config/tools/action",
    params: {
      service: "string",
    },
  },
  developer_template: {
    redirect: "/config/tools/template",
  },
  developer_events: {
    redirect: "/config/tools/event",
  },
  developer_statistics: {
    redirect: "/config/tools/statistics",
  },
  server_controls: {
    redirect: "/config/tools/yaml",
  },
  calendar: {
    component: "calendar",
    redirect: "/calendar",
  },
  companion_app: {
    redirect: "#external-app-configuration",
  },
  config: {
    redirect: "/config/dashboard",
  },
  cloud: {
    component: "cloud",
    redirect: "/config/cloud",
  },
  config_flow_start: {
    redirect: "/config/integrations/dashboard/add",
    params: {
      domain: "string",
    },
  },
  brand: {
    redirect: "/config/integrations/dashboard/add",
    params: {
      brand: "string",
    },
  },
  integration: {
    redirect: "/config/integrations/integration",
    params: {
      domain: "string",
    },
  },
  integrations: {
    redirect: "/config/integrations",
  },
  config_mqtt: {
    component: "mqtt",
    redirect: "/config/mqtt",
  },
  config_zha: {
    component: "zha",
    redirect: "/config/zha/dashboard",
  },
  config_zwave_js: {
    component: "zwave_js",
    redirect: "/config/zwave_js/dashboard",
  },
  config_matter: {
    component: "matter",
    redirect: "/config/matter/dashboard",
  },
  config_thread: {
    component: "thread",
    redirect: "/config/thread",
  },
  add_zigbee_device: {
    component: "zha",
    redirect: "/config/zha/add",
  },
  add_zwave_device: {
    component: "zwave_js",
    redirect: "/config/zwave_js/add",
  },
  add_matter_device: {
    component: "matter",
    redirect: "/config/matter/add",
  },
  bluetooth_advertisement_monitor: {
    component: "bluetooth",
    redirect: "/config/bluetooth/advertisement-monitor",
  },
  bluetooth_connection_monitor: {
    component: "bluetooth",
    redirect: "/config/bluetooth/connection-monitor",
  },
  bluetooth_visualization: {
    component: "bluetooth",
    redirect: "/config/bluetooth/visualization",
  },
  config_ai_task: {
    redirect: "/config/ai-tasks",
  },
  config_bluetooth: {
    component: "bluetooth",
    redirect: "/config/bluetooth",
  },
  config_dhcp: {
    component: "dhcp",
    redirect: "/config/dhcp",
  },
  config_energy: {
    component: "energy",
    redirect: "/config/energy",
  },
  config_infrared: {
    redirect: "/config/infrared",
  },
  config_radiofrequency: {
    component: "radio_frequency",
    redirect: "/config/radio-frequency",
  },
  config_ssdp: {
    component: "ssdp",
    redirect: "/config/ssdp",
  },
  config_zeroconf: {
    component: "zeroconf",
    redirect: "/config/zeroconf",
  },
  devices: {
    redirect: "/config/devices/dashboard",
  },
  entities: {
    redirect: "/config/entities",
  },
  labels: {
    redirect: "/config/labels",
  },
  energy: {
    component: "energy",
    redirect: "/energy",
  },
  areas: {
    redirect: "/config/areas/dashboard",
  },
  blueprint_import: {
    component: "blueprint",
    redirect: "/config/blueprint/dashboard/import",
    params: {
      blueprint_url: "url",
    },
  },
  blueprints: {
    component: "blueprint",
    redirect: "/config/blueprint/dashboard",
  },
  automations: {
    component: "automation",
    redirect: "/config/automation/dashboard",
  },
  scenes: {
    component: "scene",
    redirect: "/config/scene/dashboard",
  },
  scripts: {
    component: "script",
    redirect: "/config/script/dashboard",
  },
  helpers: {
    redirect: "/config/helpers",
  },
  labs: {
    redirect: "/config/labs",
    params: {
      domain: "string?",
      preview_feature: "string?",
    },
  },
  tags: {
    component: "tag",
    redirect: "/config/tags",
  },
  voice_assistants: {
    redirect: "/config/voice-assistants",
  },
  lovelace_dashboards: {
    component: "lovelace",
    redirect: "/config/lovelace/dashboards",
  },
  lovelace_resources: {
    component: "lovelace",
    redirect: "/config/lovelace/resources",
  },
  oauth: {
    redirect: "/auth/external/callback",
    navigate_outside_spa: true,
    params: {
      error: "string?",
      code: "string?",
      state: "string",
    },
  },
  people: {
    component: "person",
    redirect: "/config/person",
  },
  zones: {
    component: "zone",
    redirect: "/config/zone",
  },
  users: {
    redirect: "/config/users",
  },
  general: {
    redirect: "/config/general",
  },
  logs: {
    redirect: "/config/logs",
    params: {
      provider: "string?",
    },
  },
  repairs: {
    component: "repairs",
    redirect: "/config/repairs",
  },
  info: {
    redirect: "/config/info",
  },
  system_health: {
    redirect: "/config/repairs?dialog=system-health",
  },
  hardware: {
    redirect: "/config/hardware",
  },
  storage: {
    redirect: "/config/storage",
  },
  network: {
    redirect: "/config/network",
  },
  analytics: {
    redirect: "/config/analytics",
  },
  updates: {
    redirect: "/config/updates",
  },
  system_dashboard: {
    redirect: "/config/system",
  },
  customize: {
    // customize was removed in 2021.12, fallback to dashboard
    redirect: "/config/dashboard",
  },
  profile_security: {
    redirect: "/profile/security",
  },
  profile: {
    redirect: "/profile",
  },
  logbook: {
    component: "logbook",
    redirect: "/logbook",
  },
  history: {
    component: "history",
    redirect: "/history",
  },
  maintenance: {
    redirect: "/maintenance",
  },
  overview: {
    redirect: "/home/overview",
  },
  media_browser: {
    component: "media_source",
    redirect: "/media-browser",
  },
  backup_list: {
    component: "backup",
    redirect: "/config/backup/backups",
  },
  backup_config: {
    component: "backup",
    redirect: "/config/backup/settings",
  },
  backup: {
    component: "backup",
    redirect: "/config/backup",
  },
  supervisor_snapshots: {
    component: "backup",
    redirect: "/config/backup",
  },
  supervisor_backups: {
    component: "backup",
    redirect: "/config/backup",
  },
  supervisor_system: {
    // Moved from Supervisor panel in 2022.5
    redirect: "/config/system",
  },
  supervisor_logs: {
    // Moved from Supervisor panel in 2022.5
    redirect: "/config/logs?provider=supervisor",
  },
  supervisor_info: {
    // Moved from Supervisor panel in 2022.5
    redirect: "/config/info",
  },
  supervisor_store: {
    component: "hassio",
    redirect: "/config/apps/available",
  },
  supervisor_addons: {
    component: "hassio",
    redirect: "/config/apps",
  },
  supervisor_app: {
    component: "hassio",
    redirect: "/config/app",
    params: {
      app: "string",
    },
    optional_params: {
      repository_url: "url",
    },
  },
  supervisor_addon: {
    component: "hassio",
    redirect: "/config/app",
    params: {
      addon: "string",
    },
    optional_params: {
      repository_url: "url",
    },
  },
  supervisor_add_addon_repository: {
    component: "hassio",
    redirect: "/config/apps/available",
    params: {
      repository_url: "url",
    },
  },
  hacs_repository: {
    component: "hacs",
    redirect: "/hacs/_my_redirect/hacs_repository",
    params: {
      owner: "string",
      repository: "string",
      category: "string?",
    },
  },
  lights: {
    redirect: "/lights",
  },
});

const getRedirect = (path: string): Redirect | undefined =>
  getMyRedirects()?.[path];

export type ParamType = "url" | "string" | "string?";

export type Redirects = Record<string, Redirect>;
export interface Redirect {
  redirect: string;
  // Set to True to use browser redirect instead of frontend navigation
  navigate_outside_spa?: boolean;
  component?: string;
  params?: Record<string, ParamType>;
  optional_params?: Record<string, ParamType>;
}

@customElement("ha-panel-my")
class HaPanelMy extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) public route!: Route;

  @state() public _error?: string;

  private _redirect?: Redirect;

  connectedCallback() {
    super.connectedCallback();
    const path = this.route.path.substring(
      1,
      this.route.path.endsWith("/") ? this.route.path.length - 1 : undefined
    );

    this._redirect = getRedirect(path);

    if (!this._redirect) {
      this._error = "not_supported";
      return;
    }

    if (this._redirect.redirect === "#external-app-configuration") {
      if (this.hass.auth.external?.config.hasSettingsScreen) {
        this.hass.auth.external!.fireMessage({ type: "config_screen/show" });
        return;
      }
      this._error = "not_app";
      return;
    }

    if (
      this._redirect.component &&
      !isComponentLoaded(this.hass.config, this._redirect.component)
    ) {
      this.hass.loadBackendTranslation("title", this._redirect.component);
      this._error =
        this._redirect.component === "hassio"
          ? "no_supervisor"
          : "no_component";
      const component = this._redirect.component;
      if ((PROTOCOL_INTEGRATIONS as readonly string[]).includes(component)) {
        const params = extractSearchParamsObject();
        this.hass
          .loadFragmentTranslation("config")
          .then()
          .then(() => {
            protocolIntegrationPicked(this, this.hass, component, {
              domain: params.domain,
              brand: params.brand,
            });
          });
      }
      return;
    }

    let url: string;
    try {
      url = this._createRedirectUrl();
    } catch (_err: any) {
      this._error = "url_error";
      return;
    }

    if (this._redirect.navigate_outside_spa) {
      location.assign(url);
    } else {
      navigate(url, { replace: true });
    }
  }

  protected render() {
    if (this._error) {
      let error: string;
      switch (this._error) {
        case "not_supported":
          error =
            this.hass.localize("ui.panel.my.not_supported", {
              link: html`<a
                target="_blank"
                rel="noreferrer noopener"
                href="https://my.home-assistant.io/faq.html#supported-pages"
                >${this.hass.localize("ui.panel.my.faq_link")}</a
              >`,
            }) || "This redirect is not supported.";
          break;
        case "no_component":
          error =
            this.hass.localize("ui.panel.my.component_not_loaded", {
              integration: html`<a
                target="_blank"
                rel="noreferrer noopener"
                href=${documentationUrl(
                  this.hass,
                  `/integrations/${this._redirect!.component!}`
                )}
                >${domainToName(
                  this.hass.localize,
                  this._redirect!.component!
                )}</a
              >`,
            }) || "This redirect is not supported.";
          break;
        case "no_supervisor":
          error = this.hass.localize("ui.panel.my.no_supervisor", {
            docs_link: html`<a
              target="_blank"
              rel="noreferrer noopener"
              href=${documentationUrl(this.hass, "/installation")}
              >${this.hass.localize("ui.panel.my.documentation")}</a
            >`,
          });
          break;
        case "not_app":
          error = this.hass.localize("ui.panel.my.not_app", {
            link: html`<a
              target="_blank"
              rel="noreferrer noopener"
              href="https://companion.home-assistant.io/download"
              >${this.hass.localize("ui.panel.my.download_app")}</a
            >`,
          });
          break;
        case "url_error":
          error = this.hass.localize("ui.panel.my.url_error");
          break;
        default:
          error = this.hass.localize("ui.panel.my.error") || "Unknown error";
      }
      return html`<hass-error-screen
        .error=${error}
        .hass=${this.hass}
      ></hass-error-screen>`;
    }
    return nothing;
  }

  private _createRedirectUrl(): string {
    const params = extractSearchParamsObject();

    // Special case for supervisor_app/supervisor_addon: use path-based URL
    // Support both "app" (new) and "addon" (legacy) parameters
    if (this._redirect!.redirect === "/config/app") {
      const appSlug = params.app || params.addon;
      if (appSlug) {
        delete params.app;
        delete params.addon;
        const optionalParams = this._createOptionalParams(params);
        return `/config/app/${appSlug}/info${optionalParams}`;
      }
    }

    const resultParams = this._createRedirectParams();
    return `${this._redirect!.redirect}${resultParams}`;
  }

  private _createRedirectParams(): string {
    const params = extractSearchParamsObject();
    if (
      !this._redirect!.params &&
      !this._redirect!.optional_params &&
      !Object.keys(params).length
    ) {
      return "";
    }
    const resultParams = {};
    for (const [key, type] of Object.entries(this._redirect!.params || {})) {
      const value = params[key];
      if (!value && type.endsWith("?")) {
        continue;
      }
      if (!value || !this._checkParamType(type, value)) {
        throw Error();
      }
      resultParams[key] = value;
    }
    for (const [key, type] of Object.entries(
      this._redirect!.optional_params || {}
    )) {
      const value = params[key];
      if (value) {
        if (!this._checkParamType(type, value)) {
          throw Error();
        }
        resultParams[key] = value;
      }
    }
    return Object.keys(resultParams).length
      ? `?${createSearchParam(resultParams)}`
      : "";
  }

  private _createOptionalParams(params: Record<string, string>): string {
    if (!this._redirect!.optional_params) {
      return "";
    }
    const resultParams = {};
    for (const [key, type] of Object.entries(this._redirect!.optional_params)) {
      const value = params[key];
      if (value) {
        if (!this._checkParamType(type, value)) {
          throw Error();
        }
        resultParams[key] = value;
      }
    }
    return Object.keys(resultParams).length
      ? `?${createSearchParam(resultParams)}`
      : "";
  }

  private _checkParamType(type: ParamType, value: string) {
    if (type === "string" || type === "string?") {
      return true;
    }
    if (type === "url") {
      return value && value === sanitizeUrl(value);
    }
    return false;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-panel-my": HaPanelMy;
  }
}
