import { computed, ref } from 'vue';

import { api } from '#/api/request';

import { mergeConfigJsonBySourceType } from './datacenter-constants';

export function useDatacenterSources() {
  const sources = ref<any[]>([]);
  const selectedSourceId = ref<null | number>(null);
  const loading = ref(false);
  const saving = ref(false);
  const testing = ref(false);

  const selectedSource = computed(
    () =>
      sources.value.find((item) => item.id === selectedSourceId.value) || null,
  );

  function normalizeSource(record: any, previous?: any) {
    return {
      ...previous,
      ...record,
      runtimeUnavailable: Boolean(
        record?.runtimeUnavailable ?? previous?.runtimeUnavailable,
      ),
    };
  }

  function syncSelectedSourceAfterMutation() {
    if (sources.value.length === 0) {
      selectedSourceId.value = null;
      return;
    }
    if (
      !selectedSourceId.value ||
      !sources.value.some((item) => item.id === selectedSourceId.value)
    ) {
      selectedSourceId.value = sources.value[0].id;
    }
  }

  function upsertSource(record?: any) {
    if (!record?.id) return;
    const previous = sources.value.find((item) => item.id === record.id);
    const next = dedupeBuiltinSources(
      sources.value.some((item) => item.id === record.id)
        ? sources.value.map((item) =>
            item.id === record.id ? normalizeSource(record, previous) : item,
          )
        : [normalizeSource(record), ...sources.value],
    );
    sources.value = next;
    selectedSourceId.value = record.id;
  }

  function markSourceRuntimeUnavailable(sourceId?: null | number | string) {
    if (!sourceId) return;
    sources.value = sources.value.map((item) =>
      String(item.id) === String(sourceId)
        ? { ...item, runtimeUnavailable: true }
        : item,
    );
  }

  function clearSourceRuntimeUnavailable(sourceId?: null | number | string) {
    if (!sourceId) return;
    sources.value = sources.value.map((item) =>
      String(item.id) === String(sourceId)
        ? { ...item, runtimeUnavailable: false }
        : item,
    );
  }

  function removeSourceFromState(sourceId: number) {
    sources.value = sources.value.filter((item) => item.id !== sourceId);
    syncSelectedSourceAfterMutation();
  }

  function dedupeBuiltinSources(records: any[]) {
    const builtinSourceTypes = new Set(['PROJECT_MYSQL']);
    const seen = new Set<string>();

    return (records || []).filter((item) => {
      const uniqueKey = `${item.sourceType}:${item.sourceName}`;
      if (!item.builtinFlag || !builtinSourceTypes.has(item.sourceType)) {
        return true;
      }
      if (seen.has(uniqueKey)) {
        return false;
      }
      seen.add(uniqueKey);
      return true;
    });
  }

  async function loadSources() {
    const res = await api.get('/api/v1/datacenterSource/page', {
      params: { pageNumber: 1, pageSize: 200 },
    });
    const previousMap = new Map(
      sources.value.map((item) => [String(item.id), item]),
    );
    sources.value = dedupeBuiltinSources(
      (res.data?.records || []).map((record: any) =>
        normalizeSource(record, previousMap.get(String(record.id))),
      ),
    );
    syncSelectedSourceAfterMutation();
  }

  async function saveSource(form: Record<string, any>) {
    saving.value = true;
    try {
      const payload = {
        ...form,
        configJson: {
          ...mergeConfigJsonBySourceType(form.sourceType, form.configJson),
          password: form.password || undefined,
        },
      };
      const res = await api.post('/api/v1/datacenterSource/save', payload);
      upsertSource(res.data);
      return res.data;
    } finally {
      saving.value = false;
    }
  }

  async function testConnection(target: Record<string, any>) {
    testing.value = true;
    try {
      const payload = {
        ...target,
        configJson: {
          ...mergeConfigJsonBySourceType(target.sourceType, target.configJson),
          password: target.password || undefined,
        },
      };
      const res = await api.post(
        '/api/v1/datacenterSource/testConnection',
        payload,
      );
      return res.data;
    } finally {
      testing.value = false;
    }
  }

  async function quickTest(row: any) {
    testing.value = true;
    try {
      const res = await api.post(
        '/api/v1/datacenterSource/testConnection',
        row,
      );
      clearSourceRuntimeUnavailable(row?.id);
      await loadSources();
      return res.data;
    } catch (error) {
      markSourceRuntimeUnavailable(row?.id);
      throw error;
    } finally {
      testing.value = false;
    }
  }

  async function removeSource(sourceId: number) {
    await api.post('/api/v1/datacenterSource/remove', { sourceId });
    removeSourceFromState(sourceId);
  }

  return {
    sources,
    selectedSourceId,
    selectedSource,
    loading,
    saving,
    testing,
    loadSources,
    removeSource,
    removeSourceFromState,
    saveSource,
    syncSelectedSourceAfterMutation,
    testConnection,
    quickTest,
    upsertSource,
    markSourceRuntimeUnavailable,
    clearSourceRuntimeUnavailable,
  };
}
