<template>
  <q-page class="owner-dashboard-page">
    <DefaultHeaderPage />

    <div class="dashboard-shell">
      <q-banner
        v-if="!hasProperties"
        class="bg-surface text-text radius-8"
        rounded
      >
        Nenhum imóvel vinculado ao proprietário autenticado.
      </q-banner>

      <template v-else>
        <DashboardFiltersBar
          action-label="Ver despesas"
          :can-export-report="canExportReport"
          :exporting="exporting"
          :loading="loading"
          :month-options="monthOptions"
          :property-options="propertyOptions"
          :selected-month="selectedMonth"
          :selected-property-option="selectedPropertyOption"
          :selected-year="selectedYear"
          :year-options="yearOptions"
          @action="goToExpenses"
          @export="exportDashboardReport"
          @update:month="handleMonthChange"
          @update:property="handlePropertyChange"
          @update:year="handleYearChange"
        />

        <q-separator class="dashboard-separator" />

        <div class="section-header">
          <span class="dashboard-section-caption">
            Indicadores

            <q-tooltip anchor="center left" self="center right">
              Os números exibidos são referentes ao desempenho da reserva
            </q-tooltip>
          </span>
        </div>

        <div class="metrics-grid">
          <DashboardMetricCard
            v-for="card in firstRowCards"
            :key="card.label"
            :caption="card.caption"
            :label="card.label"
            :nowrap-caption="card.nowrapCaption"
            :value="card.value"
            variant="primary"
          />
        </div>

        <div class="section-header">
          <span class="dashboard-section-caption">
            Financeiro

            <q-tooltip anchor="center left" self="center right">
              Os valores exibidos, são referentes ao desempenho financeiro
            </q-tooltip>
          </span>
        </div>

        <div class="metrics-grid">
          <DashboardMetricCard
            v-for="card in secondRowCards"
            :key="card.label"
            :caption="card.caption"
            :label="card.label"
            :nowrap-caption="card.nowrapCaption"
            :value="card.value"
            variant="secondary"
          />
        </div>

        <div class="dashboard-panels">
          <DashboardHorizontalBarPanel
            title="Disponibilidade do Período"
            :items="availabilityItems"
            :total-label="availabilityTotalLabel"
          />

          <DashboardRevenuePanel
            :chart-data="revenueChartData"
            :chart-options="revenueChartOptions"
          />

          <DashboardHorizontalBarPanel
            title="Canais de Aquisição"
            :items="channelsBarItems"
            :total-label="channelsTotalLabel"
          />

          <q-card flat class="panel-card panel-card--soft">
            <div class="panel-title">Repasses por Unidade</div>

            <DashboardPayoutTable
              :rows="payoutRows"
              :total-reservations="
                Number(summary.checkout_reservations_count ?? 0)
              "
              :total-value="Number(summary.final_payout_amount ?? 0)"
            />
          </q-card>
        </div>
      </template>

      <q-inner-loading :showing="loading">
        <q-spinner color="primary" size="48px" />
      </q-inner-loading>
    </div>
  </q-page>
</template>

<script setup>
import {
  ArcElement,
  CategoryScale,
  Chart as ChartJS,
  Filler,
  Legend,
  LineElement,
  LinearScale,
  PointElement,
  Tooltip,
} from "chart.js";

import { computed, onMounted, ref } from "vue";
import { useQuasar } from "quasar";
import { useRoute, useRouter } from "vue-router";

import {
  downloadOwnerDashboardReport,
  getOwnerDashboard,
} from "src/api/ownerDashboard";

import { buildMetricCards as buildDashboardMetricCards } from "src/helpers/buildMetricCards";

import {
  formatCurrency,
  formatCurrencyCompact,
  formatInteger,
  formatPercent,
} from "src/helpers/utils";

import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
import DashboardFiltersBar from "./components/DashboardFiltersBar.vue";
import DashboardHorizontalBarPanel from "./components/DashboardHorizontalBarPanel.vue";
import DashboardMetricCard from "./components/DashboardMetricCard.vue";
import DashboardPayoutTable from "./components/DashboardPayoutTable.vue";
import DashboardRevenuePanel from "./components/DashboardRevenuePanel.vue";

ChartJS.register(
  ArcElement,
  CategoryScale,
  Filler,
  Legend,
  LineElement,
  LinearScale,
  PointElement,
  Tooltip,
);

const $q = useQuasar();
const route = useRoute();
const router = useRouter();

const dashboard = ref(null);
const loading = ref(false);
const exporting = ref(false);
const selectedPropertyOption = ref(null);
const selectedMonth = ref(null);
const selectedYear = ref(null);
const revenueSeries = ref([]);
const dashboardRequestId = ref(0);
const historyRequestId = ref(0);

const defaultSummary = Object.freeze({
  reserve_total: 0,
  total_forward_fee_all: 0,
  owner_payout_amount: 0,
  final_payout_amount: 0,
  total_expenses_amount: 0,
  occupied_nights_in_month: 0,
  occupancy_rate: 0,
  average_price_per_night: null,
  average_reservation_ticket: null,
  average_nights_per_reservation: 0,
  maintenance_days: 0,
  cleanings_count: 0,
  expenses: 0,
  reservations_count: 0,
  distinct_reservations_count: 0,
  checkout_reservations_count: 0,
  crossed_reservations_count: 0,
  repeated_reservations_count: 0,
  crossed_cleanings_count: 0,
  repeated_cleanings_count: 0,
  available_days: 0,
  days_in_month: 30,
  properties_count: 1,

  gross_revenue: 0,
  ota_amount: 0,
  net_revenue_amount: 0,
  cleaning_total_amount: 0,
  management_fee_amount: 0,
  expenses_amount: 0,
  manual_expenses_amount: 0,
  payout_amount: 0,
  expenses_breakdown: {
    items: [],
    system_expenses: [],
    totals: {
      expenses_amount: 0,
      manual_expenses_amount: 0,
      system_expenses_amount: 0,
    },
  },
});

const monthLabels = [
  "Janeiro",
  "Fevereiro",
  "Março",
  "Abril",
  "Maio",
  "Junho",
  "Julho",
  "Agosto",
  "Setembro",
  "Outubro",
  "Novembro",
  "Dezembro",
];

//

const availableReferences = computed(
  () => dashboard.value?.filters?.available_references ?? [],
);

const selectedFilters = computed(
  () => dashboard.value?.filters?.selected ?? {},
);

//

const propertyOptions = computed(() => {
  const properties = dashboard.value?.filters?.properties ?? [];

  return [
    { id: null, label: "Todos os imóveis" },
    ...properties.map((property) => ({
      id: property.id,
      label: property.label,
    })),
  ];
});

const hasProperties = computed(() => propertyOptions.value.length > 1);

const selectedPropertyId = computed(() => {
  return selectedPropertyOption.value?.id ?? null;
});

const isAllPropertiesSelected = computed(
  () => selectedPropertyId.value === null,
);

const canExportReport = computed(
  () => hasProperties.value && dashboard.value !== null,
);

//

const payoutRows = computed(() => dashboard.value?.properties_breakdown ?? []);

const summary = computed(() => dashboard.value?.summary ?? defaultSummary);

const totalCapacityDays = computed(() =>
  Math.max(
    1,
    Number(summary.value.days_in_month ?? 0) *
      Number(summary.value.properties_count ?? 1),
  ),
);

//

const yearOptions = computed(() => {
  const years = [
    ...new Set(
      availableReferences.value.map((reference) => reference.reference_year),
    ),
  ];

  return years
    .sort((a, b) => b - a)
    .map((year) => ({
      label: String(year),
      value: year,
    }));
});

const monthOptions = computed(() => {
  if (!selectedYear.value) {
    return [];
  }

  const now = new Date();
  const currentYear = now.getFullYear();
  const currentMonth = now.getMonth() + 1;

  const lastMonth =
    selectedYear.value < currentYear ? 12 : Math.min(currentMonth, 12);

  const months = Array.from({ length: lastMonth }, (_, index) => index + 1);

  return months.map((month) => ({
    label: monthLabels[month - 1] ?? String(month),
    value: month,
  }));
});

//

const allMetricCards = computed(() =>
  buildDashboardMetricCards(summary.value, {
    isAllPropertiesSelected: isAllPropertiesSelected.value,
    referenceLabel: selectedReferenceLabel.value,
    totalCapacityDays: totalCapacityDays.value,
  }),
);

const firstRowCards = computed(() => allMetricCards.value.slice(0, 7));

const secondRowCards = computed(() => allMetricCards.value.slice(7, 14));

const selectedReferenceLabel = computed(() => {
  if (!selectedMonth.value || !selectedYear.value) {
    return "Mês selecionado";
  }

  const monthLabel =
    monthLabels[selectedMonth.value - 1] ?? String(selectedMonth.value);

  return `${monthLabel} ${selectedYear.value}`;
});

//

const availabilityTotalLabel = computed(() => {
  if (isAllPropertiesSelected.value) {
    return "Total: 100% do período consolidado";
  }

  const total = Number(summary.value.days_in_month ?? 0);

  return `Total: ${formatInteger(total)} dias no período`;
});

const channelsTotalLabel = computed(() => {
  const total = Number(summary.value.reservations_count ?? 0);

  return `Total: ${formatInteger(total)} reservas`;
});

//

const availabilityItems = computed(() => {
  const total =
    Number(summary.value.days_in_month ?? 0) *
    Number(summary.value.properties_count ?? 1);

  const items = [
    {
      label: "Dias Ocupados",
      value: Number(summary.value.occupied_nights_in_month ?? 0),
      color: "#E3F4A9",
    },
    {
      label: "Dias Disponíveis",
      value: Number(summary.value.available_days ?? 0),
      color: "#7AA7AA",
    },
    {
      label: "Dias de Manutenção",
      value: Number(summary.value.maintenance_days ?? 0),
      color: "#FEDCA8",
    },
  ];

  return items.map((item, index) => ({
    key: `${item.label}-${index}`,

    label: item.label,

    valueLabel: `${formatInteger(item.value)} dias (${formatPercent(
      total > 0 ? (item.value * 100) / total : 0,
      1,
    )})`,

    percentage: total > 0 ? Number(((item.value * 100) / total).toFixed(2)) : 0,

    color: item.color,
  }));
});

const channelColorsMap = {
  "API booking.com": "#6b93cf",
  "API airbnb": "#ff8f93",
  "API Decolar": "#c4b5fd",
  "Sem canal": "#d3d3d3",
  Kizzo: "#7BB2AB",
};

const channelsBarItems = computed(() => {
  const channels = dashboard.value?.channels ?? [];

  const knownChannels = [];
  let kizzoReservations = 0;
  let kizzoShare = 0;

  for (const item of channels) {
    if (channelColorsMap[item.channel] && item.channel !== "Kizzo") {
      knownChannels.push(item);
    } else {
      kizzoReservations += item.reservations_count ?? 0;
      kizzoShare += item.share_percentage ?? 0;
    }
  }

  const items = knownChannels.map((item, index) => {
    return {
      key: `${item.channel}-${index}`,

      label: item.channel,

      valueLabel: `${formatInteger(
        item.reservations_count,
      )} reservas (${formatPercent(item.share_percentage, 1)})`,

      percentage: Number(item.share_percentage ?? 0),

      color: channelColorsMap[item.channel],
    };
  });

  if (kizzoReservations > 0) {
    items.push({
      key: "Kizzo",
      label: "Kizzo",
      valueLabel: `${formatInteger(
        kizzoReservations,
      )} reservas (${formatPercent(kizzoShare, 1)})`,
      percentage: Number(kizzoShare.toFixed(2)),
      color: channelColorsMap["Kizzo"],
    });
  }

  return items;
});

const shortMonthLabel = (month, year) => {
  if (!month || !year) {
    return "-";
  }

  return new Intl.DateTimeFormat("pt-BR", {
    month: "short",
  })
    .format(new Date(year, month - 1, 1))
    .replace(".", "");
};

const revenueChartData = computed(() => {
  const seriesRaw = revenueSeries.value.length
    ? [...revenueSeries.value]
    : [
        {
          label: shortMonthLabel(selectedMonth.value, selectedYear.value),
          value: Number(summary.value.reserve_total ?? 0),
        },
      ];

  seriesRaw.pop();

  return {
    labels: seriesRaw.map((item) => item.label),
    datasets: [
      {
        label: "Faturamento",
        data: seriesRaw.map((item) => item.value),
        borderColor: "#399FE7",
        borderWidth: 2,
        fill: false,
        tension: 0.35,
        pointBackgroundColor: "#399FE7",
        pointBorderColor: "#399FE7",
        pointRadius: 3,
        pointHoverRadius: 4,
      },
    ],
  };
});

const revenueChartOptions = computed(() => ({
  responsive: true,
  maintainAspectRatio: false,

  plugins: {
    legend: {
      display: false,
    },

    tooltip: {
      displayColors: false,

      callbacks: {
        label: (context) => formatCurrency(context.raw),
      },
    },
  },
  scales: {
    x: {
      grid: {
        display: false,
      },
      ticks: {
        color: "#7B878C",
      },
    },
    y: {
      beginAtZero: true,

      grid: {
        color: "rgba(8, 81, 76, 0.08)",
      },

      ticks: {
        color: "#7B878C",
        callback: (value) => formatCurrencyCompact(value),
      },
    },
  },
}));

//

const buildDashboardParams = ({ year, month, propertyId, format } = {}) => {
  const params = {};

  if (year) {
    params.year = year;
  }

  if (month) {
    params.month = month;
  }

  if (propertyId !== null && propertyId !== undefined) {
    params.property_id = propertyId;
  }

  if (format) {
    params.format = format;
  }

  return params;
};

const buildFallbackReportFilename = (format) => {
  const year = String(selectedYear.value ?? "0000");

  const month = String(selectedMonth.value ?? "00").padStart(2, "0");

  const propertySegment =
    selectedPropertyId.value === null
      ? "all-properties"
      : `property_${selectedPropertyId.value}`;

  return `owner_dashboard_report_${year}${month}_${propertySegment}.${format}`;
};

const downloadBlob = (blob, filename) => {
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement("a");

  link.href = url;
  link.download = filename;

  document.body.appendChild(link);

  link.click();

  document.body.removeChild(link);

  window.URL.revokeObjectURL(url);
};

const exportDashboardReport = async (format) => {
  if (!canExportReport.value) {
    return;
  }

  exporting.value = true;

  try {
    const response = await downloadOwnerDashboardReport(
      buildDashboardParams({
        year: selectedYear.value,
        month: selectedMonth.value,
        propertyId: selectedPropertyId.value,
        format,
      }),
    );

    downloadBlob(response.data, resolveReportFilename(response, format));
  } catch (error) {
    const blobMessage = await extractBlobErrorMessage(error?.response?.data);

    $q.notify({
      type: "negative",
      message:
        blobMessage ?? "Não foi possível exportar o relatório do dashboard.",
    });
  } finally {
    exporting.value = false;
  }
};

const extractBlobErrorMessage = async (blob) => {
  if (!(blob instanceof Blob)) {
    return null;
  }

  try {
    const text = await blob.text();
    const parsed = JSON.parse(text);

    return parsed?.message ?? null;
  } catch {
    return null;
  }
};

const findPropertyOptionById = (propertyId) => {
  return (
    propertyOptions.value.find((option) => option.id === propertyId) ??
    propertyOptions.value[0] ??
    null
  );
};

const resolveReportFilename = (response, format) => {
  const explicitHeader = response.headers["x-report-filename"];

  if (explicitHeader) {
    return explicitHeader;
  }

  const contentDisposition = response.headers["content-disposition"];

  const utf8Match = contentDisposition?.match(/filename\*=UTF-8''([^;]+)/i);

  if (utf8Match?.[1]) {
    return decodeURIComponent(utf8Match[1]);
  }

  const asciiMatch = contentDisposition?.match(/filename="?([^"]+)"?/i);

  if (asciiMatch?.[1]) {
    return asciiMatch[1];
  }

  return buildFallbackReportFilename(format);
};

//

const fetchDashboard = async ({ year, month, propertyId } = {}) => {
  const requestId = ++dashboardRequestId.value;

  loading.value = true;

  try {
    const payload = await getOwnerDashboard(
      buildDashboardParams({ year, month, propertyId }),
    );

    if (requestId !== dashboardRequestId.value) {
      return;
    }

    dashboard.value = payload;

    selectedPropertyOption.value = findPropertyOptionById(
      payload.filters.selected?.property_id ?? null,
    );
    selectedMonth.value = payload.filters.selected?.reference_month ?? null;
    selectedYear.value = payload.filters.selected?.reference_year ?? null;

    await fetchRevenueHistory(payload);
  } catch (error) {
    if (requestId !== dashboardRequestId.value) {
      return;
    }

    $q.notify({
      type: "negative",
      message:
        error?.response?.data?.message ??
        "Não foi possível carregar o dashboard do proprietário.",
    });
  } finally {
    if (requestId === dashboardRequestId.value) {
      loading.value = false;
    }
  }
};

const fetchRevenueHistory = async (payload) => {
  const requestId = ++historyRequestId.value;

  const references = (payload?.filters?.available_references ?? [])
    .slice(0, 6)
    .reverse();

  if (!references.length) {
    revenueSeries.value = [];
    return;
  }

  try {
    const propertyId = payload?.filters?.selected?.property_id ?? null;

    const responses = await Promise.all(
      references.map(async (reference) => {
        const historyPayload = await getOwnerDashboard(
          buildDashboardParams({
            year: reference.reference_year,
            month: reference.reference_month,
            propertyId,
          }),
        );

        return {
          label: shortMonthLabel(
            reference.reference_month,
            reference.reference_year,
          ),
          value: Number(historyPayload?.summary?.reserve_total ?? 0),
        };
      }),
    );

    if (requestId === historyRequestId.value) {
      revenueSeries.value = responses;
    }
  } catch {
    if (requestId === historyRequestId.value) {
      revenueSeries.value = [];
    }
  }
};

//

const handleMonthChange = async (month) => {
  if (month === selectedFilters.value.reference_month) {
    return;
  }

  await fetchDashboard({
    year: selectedYear.value,
    month,
    propertyId: selectedPropertyId.value,
  });
};

const handleYearChange = async (year) => {
  const availableMonth =
    availableReferences.value
      .filter((reference) => reference.reference_year === year)
      .sort((a, b) => b.reference_month - a.reference_month)[0]
      ?.reference_month ?? selectedMonth.value;

  if (
    year === selectedFilters.value.reference_year &&
    availableMonth === selectedFilters.value.reference_month
  ) {
    return;
  }

  await fetchDashboard({
    year,
    month: availableMonth,
    propertyId: selectedPropertyId.value,
  });
};

const handlePropertyChange = async (propertyOption) => {
  const nextPropertyId = propertyOption?.id ?? null;

  const currentPropertyId = selectedFilters.value.property_id ?? null;

  if (nextPropertyId === currentPropertyId) {
    return;
  }

  await fetchDashboard({
    year: selectedYear.value,
    month: selectedMonth.value,
    propertyId: nextPropertyId,
  });
};

const goToExpenses = () => {
  router.push({
    name: "DashboardExpensesPage",
    query: {
      year: selectedYear.value ?? undefined,
      month: selectedMonth.value ?? undefined,
      property_id: selectedPropertyId.value ?? undefined,
    },
  });
};

const resolveInitialNumber = (value) => {
  const numberValue = Number(value);

  return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : null;
};

//

onMounted(async () => {
  await fetchDashboard({
    year: resolveInitialNumber(route.query.year),
    month: resolveInitialNumber(route.query.month),
    propertyId: resolveInitialNumber(route.query.property_id),
  });
});
</script>

<style scoped lang="scss">
.owner-dashboard-page {
  position: relative;
}

.dashboard-shell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.dashboard-separator {
  margin-top: -4px;
  background: rgba(8, 81, 76, 0.12);
}

.dashboard-section-caption {
  margin-top: -2px;
  margin-bottom: -4px;
  color: #08514c;
  font-size: 19px;
  font-weight: 400;
  line-height: 1.1;
  vertical-align: middle;
}

.section-header {
  display: inline-flex;
  align-items: center;
  gap: 8px;

  cursor: pointer;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 10px;
  min-width: 0;
  align-items: stretch;
}

.dashboard-panels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
  min-width: 0;
}

.panel-card {
  padding: 18px;
  border-radius: 14px;
  background: #ffffff;
  border: 1px solid #d9e3e7;
  min-height: 300px;
  min-width: 0;
  vertical-align: middle;
}

.panel-card--soft {
  background: #f0f3f5;
}

.panel-card--channels {
  min-height: 0;
  align-self: start;
}

.panel-title {
  margin-bottom: 16px;
  font-size: 19px;
  font-weight: 400;
  color: #08514c;
}

.owner-dashboard-page :deep(.text-h6.text-bold) {
  display: none;
}

@media (max-width: 1650px) {
  .metrics-grid {
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 8px;
  }
}

@media (max-width: 1450px) {
  .metrics-grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

@media (max-width: 1100px) {
  .metrics-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .dashboard-panels {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 768px) {
  .metrics-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 640px) {
  .metrics-grid {
    grid-template-columns: 1fr;
    gap: 10px;
  }
}
</style>
