import { useState, useEffect } from 'react';

import { AppEvents, LoadingState } from '@grafana/data';
import { t } from '@grafana/i18n';
import { config, getBackendSrv, isFetchError, reportInteraction } from '@grafana/runtime';
import { Spinner, Stack } from '@grafana/ui';
import { appEvents } from 'app/core/app_events';
import { Page } from 'app/core/components/Page/Page';
import { type GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { ExportFormat } from 'app/features/dashboard/api/types';
import { isDashboardV1Resource, isDashboardV2Resource } from 'app/features/dashboard/api/utils';

import { type DashboardInputs, DashboardSource } from '../../types';
import { detectExportFormat, extractV1Inputs, extractV2Inputs } from '../utils/inputs';

import { ImportOverview } from './ImportOverview';
import { ImportSourceForm } from './ImportSourceForm';

const IMPORT_STARTED_EVENT_NAME = 'dashboard_import_loaded';

type RouteParams = {};
type QueryParams = { gcomDashboardId?: string };

type Props = GrafanaRouteComponentProps<RouteParams, QueryParams>;

type ImportState = {
  status: LoadingState;
  dashboard: unknown;
  dashboardUid?: string;
  inputs: DashboardInputs;
  meta: { updatedAt: string; orgName: string };
  source: DashboardSource;
  format: ExportFormat;
};

const initialState: ImportState = {
  status: LoadingState.NotStarted,
  dashboard: {},
  dashboardUid: undefined,
  inputs: { dataSources: [], constants: [], libraryPanels: [] },
  meta: { updatedAt: '', orgName: '' },
  source: DashboardSource.Json,
  format: ExportFormat.Classic,
};

export function DashboardImportK8s({ queryParams }: Props) {
  const [state, setState] = useState<ImportState>(initialState);

  // Handle gcom dashboard ID from query params on mount
  useEffect(() => {
    const { gcomDashboardId } = queryParams;
    if (gcomDashboardId) {
      fetchGcomDashboard(gcomDashboardId);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  async function fetchGcomDashboard(id: string) {
    reportInteraction(IMPORT_STARTED_EVENT_NAME, { import_source: 'gcom' });

    setState((prev) => ({ ...prev, status: LoadingState.Loading }));

    try {
      const response = await getBackendSrv().get(`/api/gnet/dashboards/${id}`);
      const dashboard = response.json;
      const format = detectExportFormat(dashboard);
      const inputs =
        format === ExportFormat.V2Resource ? await extractV2Inputs(dashboard) : await extractV1Inputs(dashboard);

      setState({
        status: LoadingState.Done,
        dashboard,
        dashboardUid: undefined,
        inputs,
        meta: { updatedAt: response.updatedAt, orgName: response.orgName },
        source: DashboardSource.Gcom,
        format,
      });
    } catch (error) {
      setState((prev) => ({ ...prev, status: LoadingState.Error }));
      if (isFetchError(error)) {
        appEvents.emit(AppEvents.alertError, ['Failed to load dashboard', error.data?.message || 'Unknown error']);
      }
    }
  }

  async function handleFileUpload(result: string | ArrayBuffer | null) {
    reportInteraction(IMPORT_STARTED_EVENT_NAME, { import_source: 'json_uploaded' });

    try {
      const json = JSON.parse(String(result));
      await processDashboardJson(json);
    } catch (error) {
      if (error instanceof Error) {
        appEvents.emit(AppEvents.alertError, ['Import failed', 'JSON -> JS Serialization failed: ' + error.message]);
      }
    }
  }

  async function handleJsonPaste(formData: { dashboardJson: string }) {
    reportInteraction(IMPORT_STARTED_EVENT_NAME, { import_source: 'json_pasted' });

    const json = JSON.parse(formData.dashboardJson);

    if ((json.spec?.elements || json.elements) && !config.featureToggles.dashboardNewLayouts) {
      appEvents.emit(AppEvents.alertError, [
        'Import failed',
        'Dashboard using new layout cannot be imported because the feature is not enabled',
      ]);
      return;
    }

    await processDashboardJson(json);
  }

  async function processDashboardJson(json: unknown) {
    setState((prev) => ({ ...prev, status: LoadingState.Loading }));

    try {
      const format = detectExportFormat(json);
      const isV2Resource = isDashboardV2Resource(json);
      // Unwrap k8s resource to get the spec, or use as-is for classic dashboards
      const dashboard = isV2Resource || isDashboardV1Resource(json) ? json.spec : json;
      const dashboardUid = isV2Resource ? json.metadata.name : undefined;
      const inputs =
        format === ExportFormat.V2Resource ? await extractV2Inputs(dashboard) : await extractV1Inputs(dashboard);

      setState({
        status: LoadingState.Done,
        dashboard,
        dashboardUid,
        inputs,
        meta: { updatedAt: '', orgName: '' },
        source: DashboardSource.Json,
        format,
      });
    } catch (error) {
      setState((prev) => ({ ...prev, status: LoadingState.Error }));
      const message = error instanceof Error ? error.message : 'Unknown error';
      appEvents.emit(AppEvents.alertError, ['Failed to process dashboard', message]);
    }
  }

  function handleGcomSubmit(formData: { gcomDashboard: string }) {
    let dashboardId;
    const match = /(^\d+$)|dashboards\/(\d+)/.exec(formData.gcomDashboard);
    if (match && match[1]) {
      dashboardId = match[1];
    } else if (match && match[2]) {
      dashboardId = match[2];
    }

    if (dashboardId) {
      fetchGcomDashboard(dashboardId);
    }
  }

  function handleCancel() {
    setState(initialState);
  }

  const pageNav = {
    text: t('manage-dashboards.unthemed-dashboard-import.text.import-dashboard', 'Import dashboard'),
    subTitle: t(
      'manage-dashboards.unthemed-dashboard-import.subTitle.import-dashboard-from-file-or-grafanacom',
      'Import dashboard from file or Grafana.com'
    ),
  };

  return (
    <Page navId="dashboards/browse" pageNav={pageNav}>
      <Page.Contents>
        {state.status === LoadingState.Loading && (
          <Stack direction="column" justifyContent="center">
            <Stack justifyContent="center">
              <Spinner size="xxl" />
            </Stack>
          </Stack>
        )}

        {(state.status === LoadingState.NotStarted || state.status === LoadingState.Error) && (
          <ImportSourceForm
            onFileUpload={handleFileUpload}
            onGcomSubmit={handleGcomSubmit}
            onJsonSubmit={handleJsonPaste}
          />
        )}

        {state.status === LoadingState.Done && (
          <ImportOverview
            dashboard={state.dashboard}
            dashboardUid={state.dashboardUid}
            inputs={state.inputs}
            meta={state.meta}
            source={state.source}
            onCancel={handleCancel}
          />
        )}
      </Page.Contents>
    </Page>
  );
}
