import type {
  DatasetRefPayload,
  ManagedDatasetOption,
  ManagedDatasetSourceOption,
} from './datasetOptions';

import huaweiIcon from '#/assets/datacenter/huawei-icon.svg';
import mysqlIcon from '#/assets/datacenter/mysql-icon.svg';
import postgresqlIcon from '#/assets/datacenter/postgresql-icon.svg';

import {
  groupManagedDatasetOptions,
  loadManagedDatasetOptions,
} from './datasetOptions';

const SOURCE_LOGO_MAP: Record<string, string> = {
  EXCEL: 'excel',
  EXCEL_MATERIALIZED: 'excel',
  GAUSSDB_NATIVE: 'gaussdb',
  GBASE_8A: 'gbase',
  GBASE_8S: 'gbase',
  MYSQL: 'mysql',
  ORACLE: 'oracle',
  POSTGRESQL: 'postgresql',
  PROJECT_MYSQL: 'mysql',
};

type NodeLike = {
  data?: Record<string, any>;
  id: string;
};

type UpdateNodeData = (
  nodeId: string,
  data:
    | ((node: Record<string, any>) => Record<string, any>)
    | Record<string, any>,
) => void;

type FlowInstance = {
  updateNodeData: UpdateNodeData;
};

type RenderContext = FlowInstance | undefined;

type RendererState = {
  loadingOptions: boolean;
  options: ManagedDatasetOption[];
  optionsLoaded: boolean;
  pickerOpen: boolean;
  sources: ManagedDatasetSourceOption[];
  tableSearchText: string;
  updateNodeData?: UpdateNodeData;
};

function getState(parent: HTMLElement): RendererState {
  const holder = parent as HTMLElement & { __datasetState?: RendererState };
  if (!holder.__datasetState) {
    holder.__datasetState = {
      pickerOpen: false,
      loadingOptions: false,
      optionsLoaded: false,
      options: [],
      sources: [],
      tableSearchText: '',
    };
  }
  return holder.__datasetState;
}

function getUpdateNodeData(parent: HTMLElement, flowInstance?: RenderContext) {
  const state = getState(parent);
  if (flowInstance?.updateNodeData) {
    state.updateNodeData = flowInstance.updateNodeData.bind(flowInstance);
  }
  return state.updateNodeData;
}

function getDatasetKey(datasetRef?: DatasetRefPayload | null) {
  const tableId = datasetRef?.tableId;
  return tableId === null || tableId === undefined ? '' : String(tableId);
}

function getSourceKey(datasetRef?: DatasetRefPayload | null) {
  const sourceId = datasetRef?.sourceId;
  return sourceId === null || sourceId === undefined ? '' : String(sourceId);
}

function createSourceOnlyDatasetRef(
  source: ManagedDatasetSourceOption,
): DatasetRefPayload {
  return {
    sourceId: source.sourceId,
    catalogId: null,
    catalogName: '',
    tableId: null,
    tableName: '',
    versionId: null,
  };
}

function escapeHtml(value?: null | number | string) {
  return String(value ?? '')
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;');
}

function getSourceLogoType(sourceType?: string) {
  return SOURCE_LOGO_MAP[sourceType || ''] || 'default';
}

function buildSourceLogo(sourceType?: string) {
  const logoType = getSourceLogoType(sourceType);
  if (logoType === 'mysql') {
    return `<img class="dataset-node-brand-image" src="${mysqlIcon}" alt="" />`;
  }
  if (logoType === 'postgresql') {
    return `<img class="dataset-node-brand-image" src="${postgresqlIcon}" alt="" />`;
  }
  if (logoType === 'gaussdb') {
    return `<img class="dataset-node-brand-image" src="${huaweiIcon}" alt="" />`;
  }
  if (logoType === 'oracle') {
    return `
      <svg class="dataset-node-brand-svg" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <rect x="4.6" y="7.2" width="14.8" height="9.6" rx="4.8" fill="#ea4335" />
        <rect x="7.1" y="9.35" width="9.8" height="5.3" rx="2.65" fill="white" />
        <rect x="8.4" y="10.65" width="7.2" height="2.7" rx="1.35" fill="#ea4335" />
      </svg>
    `;
  }
  if (logoType === 'gbase') {
    return `
      <svg class="dataset-node-brand-svg" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <circle cx="12" cy="12" r="8" fill="#10b981" />
        <path
          d="M15.85 9.2C15.1 8.02 13.8 7.3 12.38 7.3C9.94 7.3 8.1 9.14 8.1 11.98C8.1 14.87 10.05 16.7 12.56 16.7C13.88 16.7 15.03 16.18 15.88 15.15V12.55H12.45"
          stroke="white"
          stroke-linecap="round"
          stroke-linejoin="round"
          stroke-width="1.7"
        />
      </svg>
    `;
  }
  if (logoType === 'excel') {
    return `
      <svg class="dataset-node-brand-svg" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <rect x="4.4" y="4.2" width="15.2" height="15.6" rx="3.2" fill="#16a34a" />
        <path
          d="M9 9L11.1 12L9 15M15 9L12.9 12L15 15"
          stroke="white"
          stroke-linecap="round"
          stroke-linejoin="round"
          stroke-width="1.65"
        />
      </svg>
    `;
  }
  return `
    <svg class="dataset-node-brand-svg" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <ellipse cx="12" cy="6.5" rx="5.75" ry="2.5" fill="#94a3b8" />
      <path d="M6.25 6.5V12.2C6.25 13.58 8.82 14.7 12 14.7C15.18 14.7 17.75 13.58 17.75 12.2V6.5" fill="#cbd5e1" />
      <path d="M6.25 12.2V17.35C6.25 18.72 8.82 19.85 12 19.85C15.18 19.85 17.75 18.72 17.75 17.35V12.2" fill="#e2e8f0" />
    </svg>
  `;
}

function buildStyles() {
  return `
    <style>
      .dataset-node-section {
        display: flex;
        flex-direction: column;
        gap: 4px;
        margin-bottom: 10px;
      }
      .dataset-node-label {
        font-size: 12px;
        color: var(--tf-text-muted);
      }
      .dataset-node-picker-anchor {
        position: relative;
        width: 100%;
      }
      .dataset-node-picker-trigger {
        width: 100%;
        min-height: 38px;
        border: 1px solid var(--tf-border-color);
        border-radius: 8px;
        padding: 7px 10px 7px 12px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 10px;
        background: var(--tf-bg-surface);
        color: var(--tf-text-primary);
        text-align: left;
        box-sizing: border-box;
      }
      .dataset-node-picker-trigger:hover {
        border-color: var(--tf-border-color-strong);
      }
      .dataset-node-picker-trigger.is-open {
        border-color: var(--tf-color-primary);
      }
      .dataset-node-picker-value {
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 8px;
      }
      .dataset-node-picker-placeholder {
        font-size: 12px;
        color: var(--tf-text-secondary);
      }
      .dataset-node-picker-text {
        min-width: 0;
        display: flex;
        align-items: center;
      }
      .dataset-node-picker-main {
        min-width: 0;
        font-size: 12px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .dataset-node-picker-arrow {
        width: 14px;
        height: 14px;
        color: var(--tf-text-secondary);
        flex-shrink: 0;
      }
      .dataset-node-picker {
        position: absolute;
        top: calc(100% + 6px);
        left: 0;
        width: 100%;
        border: 1px solid var(--tf-border-color);
        border-radius: 8px;
        background: var(--tf-bg-surface);
        box-shadow: var(--tf-shadow-medium);
        overflow: hidden;
        z-index: 40;
      }
      .dataset-node-picker-body,
      .dataset-node-list {
        padding: 6px;
        display: flex;
        flex-direction: column;
        gap: 2px;
        max-height: 240px;
        overflow: auto;
      }
      .dataset-node-picker-item {
        width: 100%;
        border: none;
        background: transparent;
        border-radius: 6px;
        padding: 9px 10px;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        text-align: left;
        cursor: pointer;
        color: inherit;
      }
      .dataset-node-picker-item:hover {
        background: var(--tf-bg-muted);
      }
      .dataset-node-picker-item.is-active {
        background: var(--tf-bg-muted);
        color: var(--tf-color-primary);
      }
      .dataset-node-picker-item-inner {
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 8px;
      }
      .dataset-node-picker-item-main {
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: 2px;
      }
      .dataset-node-picker-item-title {
        font-size: 12px;
        color: inherit;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .dataset-node-picker-item-meta {
        display: block;
        font-size: 11px;
        line-height: 16px;
        color: var(--tf-text-secondary);
      }
      .dataset-node-empty {
        font-size: 12px;
        color: var(--tf-text-secondary);
      }
      .dataset-node-brand {
        width: 20px;
        height: 20px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
      }
      .dataset-node-brand-image,
      .dataset-node-brand-svg {
        width: 18px;
        height: 18px;
        display: block;
        object-fit: contain;
      }
      .dataset-node-list-box {
        border: 1px solid var(--tf-border-color);
        border-radius: 8px;
        background: var(--tf-bg-surface);
        overflow: hidden;
      }
      .dataset-node-search {
        width: 100%;
        border: 1px solid var(--tf-border-color);
        border-radius: 8px;
        padding: 7px 10px;
        font-size: 12px;
        line-height: 18px;
        color: var(--tf-text-primary);
        background: var(--tf-bg-surface);
        box-sizing: border-box;
      }
      .dataset-node-search:focus {
        outline: none;
        border-color: var(--tf-color-primary);
      }
    </style>
  `;
}

function filterTableOptions(
  options: ManagedDatasetOption[],
  searchText: string,
) {
  const keyword = searchText.trim().toLowerCase();
  if (!keyword) {
    return options;
  }
  return options.filter((option) => option.keywords.includes(keyword));
}

function ensureOptionsLoaded(
  state: RendererState,
  parent: HTMLElement,
  node: NodeLike,
  flowInstance: RenderContext,
  rerender: (
    parent: HTMLElement,
    node: NodeLike,
    flowInstance?: RenderContext,
  ) => void,
) {
  if (state.loadingOptions || state.optionsLoaded) {
    return;
  }
  state.loadingOptions = true;
  loadManagedDatasetOptions()
    .then((options) => {
      state.options = options;
      state.sources = groupManagedDatasetOptions(options);
      state.optionsLoaded = true;
    })
    .finally(() => {
      state.loadingOptions = false;
      rerender(parent, node, flowInstance);
    });
}

function buildPickerListItem(
  title: string,
  meta: string,
  active: boolean,
  action: string,
  extraAttr: string,
  iconHtml: string = '',
) {
  return `
    <button
      type="button"
      class="dataset-node-picker-item nopan nodrag nowheel ${active ? 'is-active' : ''}"
      data-action="${action}"
      ${extraAttr}
    >
      <span class="dataset-node-picker-item-inner">
        ${iconHtml ? `<span class="dataset-node-brand">${iconHtml}</span>` : ''}
        <span class="dataset-node-picker-item-main">
          <span class="dataset-node-picker-item-title">${escapeHtml(title)}</span>
          ${meta ? `<span class="dataset-node-picker-item-meta">${escapeHtml(meta)}</span>` : ''}
        </span>
      </span>
    </button>
  `;
}

function buildSourceList(
  options: ManagedDatasetSourceOption[],
  activeKey: string,
  emptyText: string,
) {
  if (options.length === 0) {
    return `<div class="dataset-node-empty">${emptyText}</div>`;
  }
  return options
    .map((option) =>
      buildPickerListItem(
        option.sourceName,
        `${option.tables.length} 张表`,
        activeKey === String(option.sourceId),
        'select-source',
        `data-source-id="${escapeHtml(option.sourceId)}"`,
        buildSourceLogo(option.sourceType),
      ),
    )
    .join('');
}

function buildTableList(
  options: ManagedDatasetOption[],
  activeKey: string,
  emptyText: string,
) {
  if (options.length === 0) {
    return `<div class="dataset-node-empty">${emptyText}</div>`;
  }
  return options
    .map((option) =>
      buildPickerListItem(
        option.tableName,
        `${option.sourceName} / ${option.catalogName}`,
        activeKey === String(option.datasetRef.tableId),
        'select-dataset',
        `data-table-id="${escapeHtml(option.datasetRef.tableId)}"`,
        buildSourceLogo(option.sourceType),
      ),
    )
    .join('');
}

function buildSearchSummary(currentSource?: ManagedDatasetSourceOption) {
  if (!currentSource) {
    return '<span class="dataset-node-picker-placeholder">请选择连接服务</span>';
  }
  return `
    <span class="dataset-node-brand">${buildSourceLogo(currentSource.sourceType)}</span>
    <span class="dataset-node-picker-text">
      <span class="dataset-node-picker-main">${escapeHtml(currentSource.sourceName)}</span>
    </span>
  `;
}

function bindInteractiveElements(parent: HTMLElement) {
  parent
    .querySelectorAll<HTMLElement>('button, input, select, textarea')
    .forEach((element) => {
      element.addEventListener('pointerdown', (event) =>
        event.stopPropagation(),
      );
      element.addEventListener('mousedown', (event) => event.stopPropagation());
    });
}

export function rerenderSearchNode(
  parent: HTMLElement,
  node: NodeLike,
  flowInstance?: RenderContext,
) {
  const state = getState(parent);
  const updateNodeData = getUpdateNodeData(parent, flowInstance);
  ensureOptionsLoaded(state, parent, node, flowInstance, rerenderSearchNode);

  const datasetRef = (node.data?.datasetRef ||
    null) as DatasetRefPayload | null;
  const sourceKey = getSourceKey(datasetRef);
  const currentSource = state.sources.find(
    (item) => String(item.sourceId) === sourceKey,
  );

  parent.innerHTML = `
    ${buildStyles()}
    <div class="dataset-node-section">
      <div class="dataset-node-label">连接服务</div>
      <div class="dataset-node-picker-anchor">
        <button type="button" class="dataset-node-picker-trigger nopan nodrag nowheel ${state.pickerOpen ? 'is-open' : ''}" data-action="toggle-picker">
          <span class="dataset-node-picker-value">${buildSearchSummary(currentSource)}</span>
          <span class="dataset-node-picker-arrow">
            <svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"></path></svg>
          </span>
        </button>
        ${
          state.pickerOpen
            ? `
          <div class="dataset-node-picker">
            <div class="dataset-node-picker-body">
              ${state.loadingOptions ? '<div class="dataset-node-empty">正在加载连接服务...</div>' : buildSourceList(state.sources, sourceKey, '暂无可用连接服务')}
            </div>
          </div>
        `
            : ''
        }
      </div>
    </div>
  `;

  bindInteractiveElements(parent);

  parent
    .querySelector<HTMLElement>('[data-action="toggle-picker"]')
    ?.addEventListener('click', (event) => {
      event.preventDefault();
      event.stopPropagation();
      state.pickerOpen = !state.pickerOpen;
      rerenderSearchNode(parent, node, flowInstance);
    });

  parent
    .querySelectorAll<HTMLElement>('[data-action="select-source"]')
    .forEach((element) => {
      element.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        const sourceId = element.dataset.sourceId;
        const source = state.sources.find(
          (item) => String(item.sourceId) === String(sourceId),
        );
        if (!source) {
          return;
        }
        state.pickerOpen = false;
        const nextDatasetRef = createSourceOnlyDatasetRef(source);
        node.data = {
          ...node.data,
          datasetRef: nextDatasetRef,
          sourceName: source.sourceName,
          sourceType: source.sourceType,
        };
        updateNodeData?.(node.id, {
          datasetRef: nextDatasetRef,
          sourceName: source.sourceName,
          sourceType: source.sourceType,
        });
        rerenderSearchNode(parent, node, flowInstance);
      });
    });
}

export function rerenderSaveNode(
  parent: HTMLElement,
  node: NodeLike,
  flowInstance?: RenderContext,
) {
  const state = getState(parent);
  const updateNodeData = getUpdateNodeData(parent, flowInstance);
  ensureOptionsLoaded(state, parent, node, flowInstance, rerenderSaveNode);

  const datasetRef = (node.data?.datasetRef ||
    null) as DatasetRefPayload | null;
  const activeKey = getDatasetKey(datasetRef);
  const filtered = filterTableOptions(state.options, state.tableSearchText);

  parent.innerHTML = `
    ${buildStyles()}
    <div class="dataset-node-section">
      <div class="dataset-node-label">已接入表</div>
      <input class="dataset-node-search nopan nodrag nowheel" data-role="table-search" placeholder="搜索连接 / 库 / 表" value="${escapeHtml(state.tableSearchText)}" />
      <div class="dataset-node-list-box">
        <div class="dataset-node-list">
          ${state.loadingOptions ? '<div class="dataset-node-empty">正在加载已接入表...</div>' : buildTableList(filtered, activeKey, '没有匹配的已接入表')}
        </div>
      </div>
    </div>
  `;

  bindInteractiveElements(parent);

  parent
    .querySelector<HTMLInputElement>('[data-role="table-search"]')
    ?.addEventListener('input', (event) => {
      event.stopPropagation();
      state.tableSearchText =
        (event.currentTarget as HTMLInputElement).value || '';
      rerenderSaveNode(parent, node, flowInstance);
    });

  parent
    .querySelectorAll<HTMLElement>('[data-action="select-dataset"]')
    .forEach((element) => {
      element.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        const tableId = element.dataset.tableId;
        const option = state.options.find(
          (item) => String(item.datasetRef.tableId) === String(tableId),
        );
        if (!option) {
          return;
        }
        node.data = {
          ...node.data,
          datasetRef: option.datasetRef,
          sourceName: option.sourceName,
          sourceType: option.sourceType,
        };
        updateNodeData?.(node.id, {
          datasetRef: option.datasetRef,
          sourceName: option.sourceName,
          sourceType: option.sourceType,
        });
        rerenderSaveNode(parent, node, flowInstance);
      });
    });
}
