<template>
  <TimeOption
    v-bind="{
      pagename: vpagename,
      api: api,
      timeopt: timeopt,
      filter: optfilter,
      importdata: props.importdata,
      newDataAvailable: newDataAvailable,
      params: vparams,
    }"
    ref="timeopt"
    @option="timeOption"
    @excel="exportExcel"
    @add="insert"
    @manual-refresh="manualRefresh"
    @refresh-data="refreshData"
    @import="openImportModal"
    class="mb-3"
    v-if="timeopt"
  ></TimeOption>
  <DataTable
    v-bind="{ pagename: vpagename }"
    @edit="edit"
    @insert="insert"
    @dataevent="dataEvent"
    v-if="pagedata"
  />
  <Modal
    @close="showmodal = undefined"
    v-bind="showmodal"
    v-if="showmodal"
  />
</template>

<script setup>
import TimeOption from "~/components/datatable/TimeOption";
import { useStore } from "~/stores/index";
// [FIX] Thêm onActivated, onDeactivated để xử lý KeepAlive
import { ref, watch, onBeforeUnmount, onActivated, onDeactivated } from "vue";

const emit = defineEmits(["modalevent", "dataevent", "dataUpdated"]);
const store = useStore();

const props = defineProps({
  pagename: String,
  api: String,
  setting: String,
  filter: Object,
  params: Object,
  data: Object,
  modal: Object,
  timeopt: Object,
  realtime: Object,
  importdata: Object,
});

const { $copy, $find, $findapi, $getapi, $setpage, $clone, $stripHtml, $snackbar, $dayjs } = useNuxtApp();

const showmodal = ref();
const pagedata = ref();
const newDataAvailable = ref(false);
const pendingNewData = ref(null);
const lastDataHash = ref(null);
const pollingInterval = ref(null);

let vpagename = props.pagename;
let vfilter = props.filter ? $copy(props.filter) : undefined;
let vparams = props.params ? $copy(props.params) : undefined;
let connection = undefined;
let optfilter = props.filter || (props.params ? props.params.filter : undefined);

const realtimeConfig = ref({ time: 0, update: "true" });

if (props.realtime) {
  realtimeConfig.value = {
    time: props.realtime.time || 0,
    update: props.realtime.update,
  };
}

if (vparams?.filter) {
  for (const [key, value] of Object.entries(vparams.filter)) {
    if (value.toString().indexOf("$") >= 0) {
      vparams.filter[key] = store[value.replace("$", "")].id;
    }
  }
}

const generateDataHash = (data) => {
  if (!data) return null;
  try {
    const replacer = (key, value) =>
      value && typeof value === "object" && !Array.isArray(value)
        ? Object.keys(value)
            .sort()
            .reduce((sorted, key) => {
              sorted[key] = value[key];
              return sorted;
            }, {})
        : value;

    const stringToHash = JSON.stringify(data, replacer);

    return stringToHash.split("").reduce((a, b) => {
      a = (a << 5) - a + b.charCodeAt(0);
      return a & a;
    }, 0);
  } catch (e) {
    console.error("Error generating data hash:", e);
    return null;
  }
};

// [FIX] Tách hàm dừng polling ra riêng để tái sử dụng
const stopAutoCheck = () => {
  if (pollingInterval.value) {
    clearInterval(pollingInterval.value);
    pollingInterval.value = null;
  }
};

const startAutoCheck = () => {
  // [FIX] Dừng interval cũ trước khi tạo mới, tránh tạo nhiều interval chồng nhau
  stopAutoCheck();
  if (realtimeConfig.value.time && realtimeConfig.value.time > 0) {
    pollingInterval.value = setInterval(() => checkDataChanges(), realtimeConfig.value.time * 1000);
  }
};

const checkDataChanges = async () => {
  try {
    const connlist = [];
    const conn1 = $findapi(props.api);

    if (vfilter) {
      const filter = $copy(conn1.params.filter) || {};
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value;
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf("$") >= 0) {
          filter[key] = store[value.replace("$", "")].id;
        }
      }
      conn1.params.filter = filter;
    }

    if (vparams) conn1.params = $copy(vparams);

    delete conn1.params.sort;
    delete conn1.params.values;

    // conn1.params.summary = "aggregate";
    // conn1.params.distinct_values = JSON.stringify({
    //   total_count: { type: "Count", field: "id" },
    //   last_updated: { type: "Max", field: "update_time" },
    //   last_created: { type: "Max", field: "create_time" },
    // });

    connlist.push(conn1);

    const rs = await $getapi(connlist);
    const obj = $find(rs, { name: props.api });
    const newMetadata = obj ? obj.data.rows : {};
    const newHash = generateDataHash(newMetadata);

    if (lastDataHash.value === null) {
      lastDataHash.value = newHash;
      return;
    }

    if (newHash !== lastDataHash.value) {
      lastDataHash.value = newHash;

      if (realtimeConfig.value.update === "true") {
        await loadFullDataAsync();
        emit("dataUpdated", {
          newData: store[vpagename].data,
          autoUpdate: true,
          hasChanges: true,
        });
      } else {
        newDataAvailable.value = true;
        emit("dataUpdated", {
          newData: null,
          autoUpdate: false,
          hasChanges: true,
        });
      }
    }
  } catch (error) {
    console.error("Error checking data:", error);
  }
};

const loadFullDataAsync = async () => {
  try {
    const connlist = [];
    const conn1 = $findapi(props.api);

    if (vfilter) {
      const filter = $copy(conn1.params.filter) || {};
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value;
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf("$") >= 0) {
          filter[key] = store[value.replace("$", "")].id;
        }
      }
      conn1.params.filter = filter;
    }

    if (vparams) conn1.params = $copy(vparams);

    delete conn1.params.summary;
    delete conn1.params.distinct_values;

    connlist.push(conn1);

    const rs = await $getapi(connlist);
    const obj = $find(rs, { name: props.api });
    const newData = obj ? $copy(obj.data.rows) : [];

    updateDataDisplay(newData);
  } catch (error) {
    console.error("Error loading full data:", error);
  }
};

const openImportModal = () => {
  const copy = $copy(props.importdata);
  showmodal.value = copy;
};

const updateDataDisplay = (newData) => {
  const copy = $clone(store[vpagename]);
  copy.data = newData;
  copy.update = { data: newData };
  store.commit(vpagename, copy);
  newDataAvailable.value = false;
  pendingNewData.value = null;
};

const manualRefresh = () => {
  if (pendingNewData.value) {
    updateDataDisplay(pendingNewData.value);
  }
};

const refreshData = async () => {
  stopAutoCheck();
  newDataAvailable.value = false;
  pendingNewData.value = null;

  await getApi();

  lastDataHash.value = null;
  await checkDataChanges();

  newDataAvailable.value = false;
  startAutoCheck();
};

provide("refreshData", refreshData);

watch(
  () => props.realtime,
  (newVal) => {
    if (newVal) {
      realtimeConfig.value.time = newVal.time || 0;
      realtimeConfig.value.update = newVal.update === true;
      startAutoCheck();
    }
  },
  { deep: true },
);
onDeactivated(() => {
  stopAutoCheck();
});

onActivated(() => {
  startAutoCheck();
});

onBeforeUnmount(() => {
  stopAutoCheck();
});

const timeOption = (v) => {
  if (!v) return getApi();

  if (v.filter_or) {
    if (vfilter) vfilter = undefined;
    if (vparams) {
      vparams.filter_or = v.filter_or;
    } else {
      const found = $copy($findapi(props.api));
      found.params.filter_or = v.filter_or;
      if (props.filter) {
        const filter = $copy(props.filter);
        for (const [key, value] of Object.entries(filter)) {
          if (value.toString().indexOf("$") >= 0) {
            filter[key] = store[value.replace("$", "")].id;
          }
        }
        found.params.filter = filter;
      }
      vparams = found.params;
    }
    return getApi();
  }

  let filter = vfilter ? vfilter : props.params ? props.params.filter || {} : {};
  for (const [key, value] of Object.entries(v.filter)) {
    filter[key] = value;
  }

  for (const [key, value] of Object.entries(filter)) {
    if (value.toString().indexOf("$") >= 0) {
      filter[key] = store[value.replace("$", "")].id;
    }
  }

  if (vfilter) {
    vfilter = filter;
    vparams = undefined;
  } else if (vparams) {
    vparams.filter = filter;
    vparams.filter_or = undefined;
  }

  if (!vfilter && !vparams) vfilter = filter;
  getApi();
};

const edit = (v) => {
  const copy = props.modal ? $copy(props.modal) : {};
  const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api, row: v };
  f.pagename = vpagename;
  f.row = v;
  copy.vbind = f;
  showmodal.value = copy;
};

const insert = () => {
  const copy = props.modal ? $copy(props.modal) : {};
  const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api };
  f.pagename = vpagename;
  copy.vbind = f;
  showmodal.value = copy;
};

const getApi = async () => {
  const connlist = [];
  let row = props.setting?.id ? $copy(props.setting) : undefined;

  if (!row) {
    const found = $find(
      store.settings.filter((v) => v),
      props.setting > 0 ? { id: props.setting } : { name: props.setting },
    );
    if (found) row = $copy(found);
  }

  if (!row) {
    const conn = $findapi("usersetting");
    conn.params.filter = props.setting > 0 ? { id: props.setting } : { name: props.setting };
    connlist.push(conn);
  }

  let data = props.data ? $copy(props.data) : undefined;

  if (!data) {
    const conn1 = $findapi(props.api);
    if (vfilter) {
      const filter = conn1.params.filter || {};
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value;
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf("$") >= 0) {
          filter[key] = store[value.replace("$", "")].id;
        }
      }
      conn1.params.filter = filter;
    }
    if (vparams) conn1.params = vparams;
    connection = conn1;
    connlist.push(conn1);
  }

  let obj = undefined;
  if (connlist.length > 0) {
    const rs = await $getapi(connlist);
    const ele = $find(rs, { name: "usersetting" });
    if (ele) {
      row = $find(ele.data.rows, { name: props.setting.name || props.setting });
      const copy = $copy(store.settings);
      copy.push(row);
      store.commit("settings", copy);
    }
    obj = $find(rs, { name: props.api });
    if (obj) data = $copy(obj.data.rows);
  }

  pagedata.value = $setpage(vpagename, row, obj);
  const copy = $clone(pagedata.value);
  copy.data = data;
  copy.update = { data: data };
  store.commit(vpagename, copy);
};

const dataEvent = (v, field, data) => {
  if (data?.modal) {
    const copy = $copy(data.modal);
    const f = copy.vbind ?? {};
    if (!f.api) f.api = props.api;
    f.pagename = vpagename;
    if (!f.row) f.row = v;
    copy.vbind = f;
    copy.field = field;
    showmodal.value = copy;
  }
  emit("modalevent", { name: "dataevent", data: { row: v, field } });
  emit("dataevent", v, field);
};

const exportExcel = async () => {
  if (!props.api) return;

  const found = $findapi("exportcsv");
  found.params = connection.params;
  const fields = pagedata.value.fields
    .filter((v) => (v.show && v.export !== "no") || v.export === "yes")
    .map((x) => ({ name: x.name, label: $stripHtml(x.label) }));
  found.params.fields = JSON.stringify(fields);
  found.url = connection.url.replace("data/", "exportcsv/");
  const rs = await $getapi([found]);

  if (rs === "error") {
    $snackbar("Đã xảy ra lỗi. Vui lòng thử lại.");
    return;
  }

  const url = window.URL.createObjectURL(new Blob([rs[0].data]));
  const link = document.createElement("a");
  const fileName = `${$dayjs(new Date()).format("YYYYMMDDhhmmss")}-data.csv`;
  link.href = url;
  link.setAttribute("download", fileName);
  document.body.appendChild(link);
  link.click();
  link.remove();
};

if (!props.timeopt) await getApi();
startAutoCheck();
</script>
