<template>
  <div>
    <DefaultHeaderPage
      :show-filter-icon="false"
      title="Plano de Contas"
    />

    <div class="q-px-md">
      <q-card
        bordered
        class="q-pa-md q-mt-md"
        flat
      >
        <div class="row items-center q-gutter-md q-mb-md">
          <div>
            <div class="text-h6">
              Plano de Contas
            </div>

            <div class="text-body2">
              {{ rows.length }} contas cadastradas
            </div>
          </div>

          <q-space />

          <q-btn
            v-if="canAdd"
            color="secondary"
            label="Nova Conta"
            no-caps
            unelevated
            @click="openCreate"
          />
        </div>

        <DefaultInput
          v-model="search"
          class="q-mb-md"
          clearable
          debounce="250"
          dense
          label="Buscar por código ou nome"
        >
          <template #prepend>
            <q-icon
              color="grey-6"
              name="mdi-magnify"
            />
          </template>
        </DefaultInput>

        <div class="account-tree-header">
          <div>Código</div>
          <div>Nome da Conta</div>
          <div>Tipo</div>
          <div>Ações</div>
        </div>

        <q-list>
          <FinancialPlanAccountTreeNode
            v-for="node in filteredTree"
            :key="node.id"
            :can-delete="canDelete"
            :can-edit="canEdit"
            :force-expanded="!!normalizedSearch"
            :node="node"
            @delete="onDelete"
            @edit="openEdit"
          />
        </q-list>

        <div
          v-if="filteredTree.length === 0"
          class="text-center q-pa-lg"
        >
          Nenhuma conta encontrada.
        </div>

        <q-inner-loading
          color="secondary"
          :showing="loadingParents"
        />
      </q-card>
    </div>

    <q-dialog v-model="dialog">
      <q-card
        class="dialog-form-card"
        style="min-width: 360px; max-width: 460px"
      >
        <DefaultDialogHeader
          :title="editing ? 'Editar conta' : 'Nova conta'"
          @close="dialog = false"
        />

        <DefaultForm
          ref="formRef"
          @submit="onSave"
        >
          <q-scroll-area
            ref="scrollAreaRef"
            class="dialog-form-scroll dialog-form-scroll--md"
          >
            <q-card-section class="q-gutter-sm">
              <q-option-group
                v-model="accountKind"
                color="primary"
                dense
                inline
                :options="kindOptions"
              />

              <div
                style="display: grid; gap: 8px"
                :style="{
                  gridTemplateColumns:
                    accountKind === 'parent'
                      ? 'minmax(0, 7fr) minmax(0, 5fr)'
                      : 'minmax(0, 1fr)',
                }"
              >
                <DefaultInput
                  v-model="form.code"
                  v-model:error="validationErrors.code"
                  label="Código (ex.: 1.1.01)"
                  outlined
                  :rules="[inputRules.required]"
                />

                <DefaultSelect
                  v-if="accountKind === 'parent'"
                  v-model="form.chart_type"
                  v-model:error="validationErrors.chart_type"
                  emit-value
                  label="Tipo"
                  map-options
                  outlined
                  :options="chartTypeOptions"
                  :rules="[inputRules.required]"
                />
              </div>

              <DefaultSelect
                v-if="accountKind === 'child'"
                v-model="form.parent_id"
                v-model:error="validationErrors.parent_id"
                emit-value
                label="Conta Pai"
                map-options
                option-label="label"
                option-value="id"
                outlined
                :loading="loadingParents"
                :options="parentOptions"
                :rules="[inputRules.required]"
              />

              <DefaultInput
                v-model="form.description"
                v-model:error="validationErrors.description"
                label="Descrição"
                outlined
                :rules="[inputRules.required]"
              />

              <div
                v-if="accountKind === 'child' && selectedParent"
                class="text-caption text-grey-7"
              >
                Tipo herdado do pai:

                <b>
                  {{ chartTypeLabel(selectedParent.chart_type) }}
                </b>
              </div>
            </q-card-section>
          </q-scroll-area>

          <q-card-actions
            align="right"
            class="q-px-md q-pb-md"
          >
            <q-btn
              color="primary"
              label="Cancelar"
              no-caps
              outline
              @click="dialog = false"
            />

            <q-btn
              color="primary"
              label="Salvar"
              no-caps
              type="submit"
              :loading="saving"
            />
          </q-card-actions>
        </DefaultForm>
      </q-card>
    </q-dialog>
  </div>
</template>

<script setup>
import { computed, onMounted, ref, useTemplateRef } from "vue";

import {
  createPlanAccountMe,
  deletePlanAccountMe,
  getPlanAccountsMe,
  updatePlanAccountMe,
} from "src/api/financial_plan_account";

import { permissionStore } from "src/stores/permission";
import { useForm } from "src/composables/useForm";
import { useInputRules } from "src/composables/useInputRules";
import { useQuasar } from "quasar";
import { useScroll } from "src/composables/useScroll";
import { useSubmitHandler } from "src/composables/useSubmitHandler";

import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
import DefaultForm from "src/components/defaults/DefaultForm.vue";
import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
import DefaultInput from "src/components/defaults/DefaultInput.vue";
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
import FinancialPlanAccountTreeNode from "src/components/financial/FinancialPlanAccountTreeNode.vue";

const $q = useQuasar();
const { inputRules } = useInputRules();
const { scrollToComponent } = useScroll();

const permissions = permissionStore();

const formRef = useTemplateRef("formRef");
const scrollAreaRef = useTemplateRef("scrollAreaRef");

const accountKind = ref("parent");
const dialog = ref(false);
const editing = ref(false);
const editingId = ref(null);
const loadingParents = ref(false);
const rows = ref([]);
const search = ref("");

const canAdd = computed(() =>
  permissions.getAccess("franchisee_financial", "add"),
);

const canDelete = computed(() =>
  permissions.getAccess("franchisee_financial", "delete"),
);

const canEdit = computed(() =>
  permissions.getAccess("franchisee_financial", "edit"),
);

const accountTree = computed(() => {
  const nodes = new Map(
    rows.value.map((account) => [
      account.id,
      {
        ...account,
        children: [],
      },
    ]),
  );

  const roots = [];

  for (const node of nodes.values()) {
    const parent = node.parent_id
      ? nodes.get(node.parent_id)
      : null;

    if (parent) {
      parent.children.push(node);

      continue;
    }

    roots.push(node);
  }

  const sortAccounts = (accounts) => {
    accounts.sort((firstAccount, secondAccount) =>
      String(firstAccount.code).localeCompare(
        String(secondAccount.code),
        "pt-BR",
        {
          numeric: true,
        },
      ),
    );

    accounts.forEach((account) => {
      sortAccounts(account.children);
    });

    return accounts;
  };

  return sortAccounts(roots);
});

const filteredTree = computed(() => {
  if (!normalizedSearch.value) return accountTree.value;

  const filterAccounts = (accounts) =>
    accounts.reduce((result, account) => {
      const children = filterAccounts(account.children);

      const matches = `${account.code} ${account.description}`
        .toLocaleLowerCase("pt-BR")
        .includes(normalizedSearch.value);

      if (matches || children.length) {
        result.push({
          ...account,
          children,
        });
      }

      return result;
    }, []);

  return filterAccounts(accountTree.value);
});

const normalizedSearch = computed(
  () => search.value?.trim().toLocaleLowerCase("pt-BR") ?? "",
);

const parentOptions = computed(() =>
  rows.value
    .filter((account) => account.id !== editingId.value)
    .map((account) => ({
      chart_type: account.chart_type,
      id: account.id,
      label: `${account.code} — ${account.description}`,
    })),
);

const selectedParent = computed(() =>
  parentOptions.value.find(
    (option) => option.id === form.parent_id,
  ),
);

const chartTypeOptions = [
  {
    label: "Receita",
    value: "receita",
  },
  {
    label: "Despesa",
    value: "despesa",
  },
];

const defaultForm = () => ({
  chart_type: "despesa",
  code: "",
  description: "",
  parent_id: null,
});

const kindOptions = [
  {
    label: "Conta Pai",
    value: "parent",
  },
  {
    label: "Conta Filho",
    value: "child",
  },
];

const { form, getUpdatedFields } = useForm(defaultForm());

const {
  loading: saving,
  validationErrors,
  execute,
} = useSubmitHandler({
  containerRef: scrollAreaRef,
  formRef,
  onSuccess: async () => {
    dialog.value = false;

    await loadData();
  },
  scrollFn: scrollToComponent,
});

const chartTypeLabel = (type) =>
  chartTypeOptions.find((option) => option.value === type)?.label ??
  type;

const loadData = async () => {
  loadingParents.value = true;

  try {
    rows.value = await getPlanAccountsMe();
  } catch (error) {
    console.error("Erro ao carregar plano de contas:", error);
  } finally {
    loadingParents.value = false;
  }
};

const onDelete = (id) => {
  const account = rows.value.find((row) => row.id === id);

  if (account && !account.unit_id) {
    $q.notify({
      message:
        "O plano de contas da Matriz não pode ser excluído pela unidade.",
      type: "negative",
    });

    return;
  }

  $q.dialog({
    cancel: true,
    message: "Tem certeza que deseja excluir esta conta do plano?",
    persistent: true,
    title: "Excluir conta",
  }).onOk(async () => {
    try {
      await deletePlanAccountMe(id);

      await loadData();
    } catch (error) {
      console.error(error);
    }
  });
};

const onSave = async () => {
  const payload = {
    code: form.code,
    description: form.description,
  };

  if (accountKind.value === "child") {
    payload.parent_id = form.parent_id;
  } else {
    payload.chart_type = form.chart_type;
    payload.parent_id = null;
  }

  await execute(() => {
    if (!editing.value) {
      return createPlanAccountMe(payload);
    }

    const changedFields = getUpdatedFields.value;
    const updatePayload = {};

    for (const key of [
      "code",
      "description",
      "chart_type",
      "parent_id",
    ]) {
      if (key in changedFields) {
        updatePayload[key] = payload[key];
      }
    }

    return updatePlanAccountMe(
      editingId.value,
      updatePayload,
    );
  });
};

const openCreate = () => {
  accountKind.value = "parent";
  editing.value = false;
  editingId.value = null;

  Object.assign(form, defaultForm());

  dialog.value = true;
};

const openEdit = (account) => {
  accountKind.value = account.parent_id ? "child" : "parent";
  editing.value = true;
  editingId.value = account.id;

  Object.assign(form, {
    chart_type: account.chart_type,
    code: account.code,
    description: account.description,
    parent_id: account.parent_id ?? null,
  });

  dialog.value = true;
};

onMounted(loadData);
</script>

<style scoped>
.account-tree-header {
  display: grid;
  grid-template-columns: 15% 1fr 16% 105px;
  gap: 16px;
  padding: 12px 56px 12px 16px;
  border-bottom: 1px solid #c7c7c7;
  font-weight: 600;
}
</style>