{% extends "base.html" %}

{% block title %}연도별 수익/비용{% endblock %}

{% block head_extra %}
<style>
    .summary-layout {
        display: grid;
        grid-template-columns: 320px minmax(0, 1fr);
        gap: 16px;
        align-items: stretch;
    }

    .summary-panel {
        display: grid;
        grid-template-columns: 320px minmax(0, 1fr);
        gap: 16px;
        align-items: stretch;
    }

    .filter-grid {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 8px;
        align-items: end;
    }

    .legend {
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        margin-top: 0;
        justify-content: center;
    }

    .legend-item {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        color: #2f3c49;
        font-size: 13px;
        font-weight: 800;
        letter-spacing: -0.01em;
        background: rgba(255,255,255,0.92);
        border: 1px solid var(--line);
        border-radius: 12px;
        padding: 8px 12px;
    }

    .legend-swatch {
        width: 10px;
        height: 10px;
        border-radius: 999px;
    }

    .chart-box {
        background:
            linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,247,249,0.98)),
            radial-gradient(circle at top left, rgba(17, 17, 17, 0.045), transparent 36%);
        border: 1px solid var(--line);
        border-radius: 16px;
        padding: 18px 20px 20px;
        box-shadow: inset 0 1px 0 rgba(255,255,255,0.92);
    }

    .chart-legend {
        margin-bottom: 12px;
    }

    .chart-svg {
        width: 100%;
        height: auto;
        aspect-ratio: 1560 / 620;
        min-height: 460px;
        display: block;
    }

    .expense-chart-svg {
        aspect-ratio: 1560 / 660;
        min-height: 510px;
    }

    .chart-note {
        margin-top: 10px;
        color: var(--muted);
        font-size: 13px;
        line-height: 1.6;
    }

    .summary-overview {
        display: grid;
        grid-template-rows: auto auto 1fr;
        gap: 14px;
        min-height: 100%;
    }

    .metric-grid {
        display: grid;
        grid-template-columns: 1fr;
        gap: 10px;
        align-content: stretch;
    }

    .chart-stack {
        display: grid;
        gap: 16px;
        min-height: 100%;
    }

    .summary-overview .stat-card,
    .chart-stack .stat-card {
        min-height: 112px;
    }

    .summary-overview .section-title h2,
    .chart-stack .section-title h2 {
        font-size: 17px;
    }

    .summary-overview .field select {
        min-width: 0;
        padding: 10px 12px;
        font-size: 13px;
    }

    .summary-overview .stat-card {
        padding: 14px 14px 15px;
        border-radius: 12px;
        gap: 4px;
    }

    .summary-overview .stat-card .label {
        font-size: 12px;
    }

    .summary-overview .stat-card .value {
        font-size: 22px;
        line-height: 1.18;
    }

    @media (max-width: 1000px) {
        .summary-layout,
        .summary-panel,
        .filter-grid,
        .metric-grid {
            grid-template-columns: 1fr;
        }
    }
</style>
{% endblock %}

{% block content %}
<section class="panel">
    <div class="section-title" style="margin-bottom: 14px;">
        <h2>수익/비용 현황</h2>
    </div>
    <div class="summary-panel">
        <div class="summary-overview">
            <div class="filter-grid">
                <div class="field">
                    <select id="granularity" aria-label="보기 기준">
                        <option value="yearly">연간</option>
                        <option value="monthly">월간</option>
                    </select>
                </div>
                <div class="field">
                    <select id="yearFilter" aria-label="연도 선택">
                        <option value="recent10">최근 10개년</option>
                        {% for year in available_years %}
                            <option value="{{ year }}">{{ year }}</option>
                        {% endfor %}
                    </select>
                </div>
            </div>
            <div class="metric-grid" id="metricGrid"></div>
        </div>
        <div class="chart-stack">
            <div class="chart-box">
                <div class="section-title" style="margin-bottom: 10px;">
                    <h2>비용 구조</h2>
                </div>
                <div class="legend chart-legend" id="expenseLegend"></div>
                <svg id="expenseChart" class="chart-svg expense-chart-svg" viewBox="0 0 1560 660" preserveAspectRatio="xMidYMid meet"></svg>
            </div>
            <div class="chart-box">
                <div class="section-title" style="margin-bottom: 10px;">
                    <h2>수금/비용/영업수지 그래프</h2>
                </div>
                <div class="legend chart-legend" id="balanceLegend"></div>
                <svg id="balanceChart" class="chart-svg" viewBox="0 0 1560 620" preserveAspectRatio="xMidYMid meet"></svg>
            </div>
        </div>
    </div>
</section>
{% endblock %}

{% block script %}
<script>
    const yearlySeries = {{ yearly_financial_series | tojson }};
    const monthlySeries = {{ monthly_financial_series | tojson }};
    const availableYears = [...new Set(yearlySeries.map((item) => item.year).filter((year) => year !== null && year !== undefined))];
    const annualMetricCards = {{ annual_metric_cards | tojson }};
    const annualExpenseChartMetrics = {{ annual_expense_chart_metrics | tojson }};
    const annualBalanceChartMetrics = {{ annual_balance_chart_metrics | tojson }};

    const palette = Object.fromEntries(
        [...annualExpenseChartMetrics, ...annualBalanceChartMetrics].map((option) => [
            option.item_key,
            option.value,
        ]),
    );

    const labels = Object.fromEntries(
        annualMetricCards.map((option) => [option.item_key, option.label]),
    );

    function formatNumber(value) {
        return new Intl.NumberFormat("ko-KR", { maximumFractionDigits: 0 }).format(value || 0);
    }

    function formatAxisLabel(value) {
        const numeric = Number(value || 0);
        if (!numeric) return "0.0";
        const sign = numeric < 0 ? "-" : "";
        const absolute = Math.abs(numeric);
        if (absolute >= 100000000) return `${sign}${(absolute / 100000000).toFixed(1)}억`;
        if (absolute >= 1000000) return `${sign}${(absolute / 1000000).toFixed(1)}백만`;
        if (absolute >= 1000) return `${sign}${(absolute / 1000).toFixed(1)}천`;
        return formatNumber(numeric);
    }

    function pickTickStep(maxValue) {
        const baseUnit = maxValue < 1000000 ? 1000 : 1000000;
        const units = [1, 2, 5];
        const raw = Math.max(maxValue / baseUnit, 1);
        let power = 1;
        while (power * 10 <= raw) power *= 10;
        for (const unit of units) {
            const candidate = unit * power;
            if (candidate >= raw) return candidate * baseUnit;
        }
        return power * 10 * baseUnit;
    }

    function buildPositiveAxisScale(maxValue, tickCount = 4) {
        const safeMax = Math.max(Number(maxValue || 0), 1);
        const paddedMax = safeMax * (safeMax < 1000 ? 1.12 : 1.08);
        const tickStep = pickTickStep(paddedMax / tickCount);
        const tickMax = Math.max(tickStep, Math.ceil(paddedMax / tickStep) * tickStep);
        return { tickStep, tickMax };
    }

    function buildDynamicPositiveAxis(maxValue, width, height, margin) {
        const safeMax = Math.max(Number(maxValue || 0), 1);
        const paddedMax = safeMax * 1.08;
        const tickStep = pickTickStep(paddedMax / 5);
        const tickMax = Math.max(tickStep, Math.ceil(paddedMax / tickStep) * tickStep);
        let axis = "";
        for (let value = 0; value <= tickMax; value += tickStep) {
            const y = height - margin.bottom - ((height - margin.top - margin.bottom) * value) / tickMax;
            axis += `<line x1="${margin.left}" y1="${y}" x2="${width - margin.right}" y2="${y}" stroke="#d8dee5" stroke-dasharray="4 8" />`;
            axis += `<text x="${margin.left - 14}" y="${y + 5}" text-anchor="end" fill="#52606d" font-size="14" font-weight="800">${formatAxisLabel(value)}</text>`;
        }
        axis += `<line x1="${margin.left}" y1="${height - margin.bottom}" x2="${width - margin.right}" y2="${height - margin.bottom}" stroke="#8ba0ae" stroke-width="1.4" />`;
        return { axis, tickStep, tickMax };
    }

    function buildDynamicBalanceAxis(maxPositiveValue, minNegativeValue, width, height, margin) {
        const safePositive = Math.max(Number(maxPositiveValue || 0), 0);
        const safeNegative = Math.min(Number(minNegativeValue || 0), 0);
        const paddedPositive = safePositive > 0 ? safePositive * 1.08 : 1000;
        const paddedNegative = safeNegative < 0 ? safeNegative * 1.08 : 0;
        const rangeAbs = Math.max(Math.abs(paddedPositive), Math.abs(paddedNegative), 1);
        const tickStep = pickTickStep(rangeAbs / 4);
        const positiveMax = Math.max(tickStep, Math.ceil(paddedPositive / tickStep) * tickStep);
        const negativeMin = safeNegative < 0 ? Math.min(-tickStep, Math.floor(paddedNegative / tickStep) * tickStep) : 0;
        const plotHeight = height - margin.top - margin.bottom;
        const totalRange = positiveMax - negativeMin;
        const zeroY = margin.top + (plotHeight * positiveMax) / totalRange;
        let axis = "";
        for (let value = negativeMin; value <= positiveMax; value += tickStep) {
            const y = margin.top + ((positiveMax - value) / totalRange) * plotHeight;
            axis += `<line x1="${margin.left}" y1="${y}" x2="${width - margin.right}" y2="${y}" stroke="#d8dee5" stroke-dasharray="4 8" />`;
            axis += `<text x="${margin.left - 14}" y="${y + 5}" text-anchor="end" fill="#52606d" font-size="14" font-weight="800">${formatAxisLabel(value)}</text>`;
        }
        axis += `<line x1="${margin.left}" y1="${zeroY}" x2="${width - margin.right}" y2="${zeroY}" stroke="#7c8d9a" stroke-width="1.5" />`;
        return { axis, tickStep, positiveMax, negativeMin, zeroY, totalRange };
    }

    function renderLegend(targetId, keys) {
        const target = document.getElementById(targetId);
        if (!target) return;
        target.innerHTML = keys.map((key) => `
            <span class="legend-item">
                <span class="legend-swatch" style="background:${palette[key]};"></span>
                ${labels[key]}
            </span>
        `).join("");
    }

    function getLatestAvailableYear() {
        return String(availableYears[availableYears.length - 1] || "recent10");
    }

    function syncYearFilter() {
        const granularity = document.getElementById("granularity").value;
        const yearFilterEl = document.getElementById("yearFilter");
        if (!yearFilterEl) return;

        if (granularity === "yearly") {
            yearFilterEl.value = "recent10";
            yearFilterEl.disabled = true;
            return;
        }

        const hasSelectedYear = availableYears.some((year) => String(year) === String(yearFilterEl.value));
        if (yearFilterEl.value === "recent10" || !hasSelectedYear) {
            yearFilterEl.value = getLatestAvailableYear();
        }
        yearFilterEl.disabled = false;
    }

    function getFilteredSeries() {
        const granularity = document.getElementById("granularity").value;
        const selectedYear = document.getElementById("yearFilter").value;
        if (granularity === "yearly" || selectedYear === "recent10") {
            return yearlySeries
                .slice(-10)
                .map((item) => ({ ...item, label: String(item.year) }));
        }
        return monthlySeries
            .filter((item) => String(item.year) === String(selectedYear))
            .map((item) => ({ ...item, label: `${item.month}월` }));
    }

    function renderMetrics(series) {
        const keys = annualMetricCards.map((option) => option.item_key);
        const totals = {};
        keys.forEach((key) => {
            totals[key] = series.reduce((sum, item) => sum + (item[key] || 0), 0);
        });
        const grid = document.getElementById("metricGrid");
        grid.innerHTML = keys.map((key) => `
            <div class="stat-card">
                <div class="label">${labels[key]}</div>
                <div class="value">${formatNumber(totals[key])}</div>
            </div>
        `).join("");
    }

    function buildAxis(maxValue, width, height, margin) {
        const { tickStep, tickMax } = buildPositiveAxisScale(maxValue, 4);
        let axis = "";
        for (let value = 0; value <= tickMax; value += tickStep) {
            const y = height - margin.bottom - ((height - margin.top - margin.bottom) * value) / tickMax;
            axis += `<line x1="${margin.left}" y1="${y}" x2="${width - margin.right}" y2="${y}" stroke="#d8dee5" stroke-dasharray="3 7" />`;
            axis += `<text x="${margin.left - 12}" y="${y + 4}" text-anchor="end" fill="#5a6672" font-size="11" font-weight="700">${formatAxisLabel(value)}</text>`;
        }
        axis += `<line x1="${margin.left}" y1="${height - margin.bottom}" x2="${width - margin.right}" y2="${height - margin.bottom}" stroke="#8ba0ae" stroke-width="1.2" />`;
        return { axis, tickMax };
    }

    function renderEmptyChart(svgId, message) {
        const svg = document.getElementById(svgId);
        if (!svg) return;
        const viewBox = (svg.getAttribute("viewBox") || "0 0 1400 560").split(/\s+/).map(Number);
        const width = viewBox[2] || 1400;
        const height = viewBox[3] || 560;
        svg.innerHTML = `
            <rect x="0" y="0" width="${width}" height="${height}" rx="8" fill="#f7fafb" stroke="#d6e2e8"></rect>
            <text x="${width / 2}" y="${height / 2}" text-anchor="middle" fill="#667887" font-size="24" font-weight="800">${message}</text>
        `;
    }

    function renderExpenseChart(series) {
        const svg = document.getElementById("expenseChart");
        const keys = annualExpenseChartMetrics.map((option) => option.item_key);
        const isMonthlyView = series.some((item) => String(item.label || "").includes("월"));
        renderLegend("expenseLegend", keys);
        if (!series.length) {
            renderEmptyChart("expenseChart", "표시할 비용 구조 데이터가 없습니다.");
            return;
        }

        const width = 1560;
        const height = 660;
        svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
        const margin = { top: 54, right: 36, bottom: 96, left: 124 };
        const plotWidth = width - margin.left - margin.right;
        const plotHeight = height - margin.top - margin.bottom;
        const barWidth = (plotWidth / series.length) * (isMonthlyView ? 0.36 : 0.42);
        const step = (width - margin.left - margin.right) / series.length;
        const maxValue = Math.max(...series.map((item) => keys.reduce((sum, key) => sum + (item[key] || 0), 0)), 1);
        const { axis, tickMax } = buildDynamicPositiveAxis(maxValue, width, height, margin);
        let markup = `
            <defs>
                <filter id="expenseShadow" x="-20%" y="-20%" width="140%" height="160%">
                    <feDropShadow dx="0" dy="8" stdDeviation="8" flood-color="rgba(44, 68, 89, 0.12)" />
                </filter>
            </defs>
            <rect x="${margin.left}" y="${margin.top}" width="${plotWidth}" height="${plotHeight}" rx="6" fill="rgba(255,255,255,0.7)" stroke="#dde7ec"></rect>
            ${axis}
        `;
        series.forEach((item, index) => {
            let cumulative = 0;
            const total = keys.reduce((sum, key) => sum + (item[key] || 0), 0);
            const x = margin.left + index * step + (step - barWidth) / 2;
            keys.forEach((key) => {
                const value = item[key] || 0;
                const barHeight = (plotHeight * value) / tickMax;
                const y = height - margin.bottom - barHeight - (plotHeight * cumulative) / tickMax;
                cumulative += value;
                markup += `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" rx="2" fill="${palette[key]}" filter="url(#expenseShadow)" />`;
                if (value > 0 && barHeight >= 34) {
                    const percent = total ? `${((value / total) * 100).toFixed(1)}%` : "0.0%";
                    markup += `<text x="${x + barWidth / 2}" y="${y + Math.min(barHeight / 2, 22)}" text-anchor="middle" fill="#ffffff" font-size="${isMonthlyView ? 12.5 : 13.5}" font-weight="900">${percent}</text>`;
                }
            });
            if (total > 0) {
                const topY = height - margin.bottom - (plotHeight * total) / tickMax;
                markup += `<text x="${x + barWidth / 2}" y="${Math.max(topY - 14, margin.top + 18)}" text-anchor="middle" fill="#2d3c4a" font-size="${isMonthlyView ? 14 : 15.5}" font-weight="900">${formatAxisLabel(total)}</text>`;
            }
            markup += `<text x="${x + barWidth / 2}" y="${height - margin.bottom + 30}" text-anchor="middle" fill="#53606c" font-size="${isMonthlyView ? 14 : 15}" font-weight="800">${item.label}</text>`;
        });
        svg.innerHTML = markup;
    }

    function renderBalanceChart(series) {
        const svg = document.getElementById("balanceChart");
        const metrics = annualBalanceChartMetrics.map((option) => option.item_key);
        renderLegend("balanceLegend", metrics);
        if (!series.length) {
            renderEmptyChart("balanceChart", "표시할 수익/비용 데이터가 없습니다.");
            return;
        }

        const width = 1560;
        const height = 620;
        svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
        const margin = { top: 56, right: 34, bottom: 100, left: 124 };
        const plotWidth = width - margin.left - margin.right;
        const plotHeight = height - margin.top - margin.bottom;
        const maxPositiveValue = Math.max(...series.flatMap((item) => [
            item.revenue_sum || 0,
            item.total_expense || 0,
            Math.max(item.operating_balance || 0, 0),
        ]), 1);
        const minNegativeValue = Math.min(...series.map((item) => Math.min(item.operating_balance || 0, 0)), 0);
        const { axis, positiveMax, negativeMin, zeroY, totalRange } = buildDynamicBalanceAxis(maxPositiveValue, minNegativeValue, width, height, margin);
        const groupWidth = plotWidth / Math.max(series.length, 1);
        const groupGap = groupWidth * 0.22;
        const innerGap = 0;
        const barWidth = Math.min((groupWidth - groupGap * 2) / metrics.length, 72);
        const actualGroupWidth = barWidth * metrics.length + innerGap * (metrics.length - 1);
        const groupStartOffset = (groupWidth - actualGroupWidth) / 2;
        let markup = `
            <defs>
                <filter id="balanceShadow" x="-20%" y="-20%" width="140%" height="160%">
                    <feDropShadow dx="0" dy="8" stdDeviation="8" flood-color="rgba(44, 68, 89, 0.12)" />
                </filter>
            </defs>
            <rect x="${margin.left}" y="${margin.top}" width="${plotWidth}" height="${plotHeight}" rx="6" fill="rgba(255,255,255,0.7)" stroke="#dde7ec"></rect>
            ${axis}
        `;
        series.forEach((item, index) => {
            const baseX = margin.left + index * groupWidth;
            const positiveLabels = [];
            const negativeLabels = [];
            metrics.forEach((key, metricIndex) => {
                const rawValue = Number(item[key] || 0);
                const value = key === "operating_balance" ? rawValue : Math.max(rawValue, 0);
                const clampedValue = Math.max(negativeMin, Math.min(positiveMax, value));
                const barHeight = (plotHeight * Math.abs(clampedValue)) / totalRange;
                const x = baseX + groupStartOffset + metricIndex * (barWidth + innerGap);
                const y = clampedValue < 0 ? zeroY : zeroY - barHeight;
                markup += `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" rx="2" fill="${palette[key]}" filter="url(#balanceShadow)" />`;
                if (clampedValue !== 0) {
                    const label = {
                        x: x + barWidth / 2,
                        desiredY: clampedValue < 0
                            ? Math.min(y + barHeight + 22, height - margin.bottom + 12)
                            : Math.max(y - 14, margin.top + 18),
                        text: formatAxisLabel(value),
                    };
                    if (clampedValue < 0) {
                        negativeLabels.push(label);
                    } else {
                        positiveLabels.push(label);
                    }
                }
            });
            positiveLabels.sort((a, b) => a.desiredY - b.desiredY);
            let lastPositiveY = margin.top - 24;
            positiveLabels.forEach((label) => {
                const y = Math.max(label.desiredY, lastPositiveY + 18);
                lastPositiveY = y;
                markup += `<text x="${label.x}" y="${y}" text-anchor="middle" fill="#2d3c4a" font-size="13.5" font-weight="900">${label.text}</text>`;
            });
            negativeLabels.sort((a, b) => a.desiredY - b.desiredY);
            let lastNegativeY = zeroY + 18;
            negativeLabels.forEach((label) => {
                const y = Math.max(label.desiredY, lastNegativeY + 18);
                lastNegativeY = y;
                markup += `<text x="${label.x}" y="${Math.min(y, height - margin.bottom + 34)}" text-anchor="middle" fill="#2d3c4a" font-size="13.5" font-weight="900">${label.text}</text>`;
            });
            markup += `<text x="${baseX + groupWidth / 2}" y="${height - margin.bottom + 34}" text-anchor="middle" fill="#53606c" font-size="15" font-weight="800">${item.label}</text>`;
        });

        svg.innerHTML = markup;
    }

    function renderAll() {
        syncYearFilter();
        const series = getFilteredSeries();
        renderMetrics(series);
        renderExpenseChart(series);
        renderBalanceChart(series);
    }

    document.getElementById("granularity").addEventListener("change", renderAll);
    document.getElementById("yearFilter").addEventListener("change", renderAll);
    const granularitySelect = document.getElementById("granularity");
    if (granularitySelect) {
        granularitySelect.value = "yearly";
    }
    const yearFilter = document.getElementById("yearFilter");
    if (yearFilter) {
        yearFilter.value = "recent10";
    }
    renderAll();
</script>
{% endblock %}
