{% extends "base.html" %}

{% block title %}대시보드{% endblock %}

{% block head_extra %}
<style>
    .dashboard-topbar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 14px;
        flex-wrap: wrap;
        margin-bottom: 16px;
    }

    .dashboard-topbar-actions {
        display: inline-flex;
        align-items: center;
        gap: 10px;
        flex-wrap: wrap;
    }

    .dashboard-year-select {
        width: 180px;
    }

    .upload-actions {
        position: relative;
        display: inline-flex;
        align-items: center;
    }

    .upload-actions:hover .upload-tooltip,
    .upload-actions:focus-within .upload-tooltip {
        opacity: 1;
        transform: translateY(0);
        pointer-events: auto;
    }

    .upload-tooltip {
        position: absolute;
        top: calc(100% + 12px);
        right: 0;
        width: 280px;
        background: rgba(20, 20, 20, 0.96);
        color: #f8fbfd;
        border-radius: 14px;
        padding: 14px 16px;
        font-size: 13px;
        line-height: 1.7;
        box-shadow: 0 18px 35px rgba(20, 20, 20, 0.18);
        opacity: 0;
        transform: translateY(-6px);
        pointer-events: none;
        transition: opacity 0.18s ease, transform 0.18s ease;
        z-index: 10;
    }

    .upload-tooltip::before {
        content: "";
        position: absolute;
        top: -8px;
        right: 22px;
        border-left: 8px solid transparent;
        border-right: 8px solid transparent;
        border-bottom: 8px solid rgba(20, 20, 20, 0.96);
    }

    .hidden-file-input {
        display: none;
    }

    .dashboard-layout {
        display: grid;
        grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
        gap: 16px;
        align-items: stretch;
    }

    .dashboard-status-panel {
        display: grid;
        gap: 12px;
    }

    .dashboard-status-grid {
        display: grid;
        grid-template-columns: 1fr;
        gap: 12px;
    }

    .dashboard-status-grid .stat-card {
        min-height: 84px;
    }

    .dashboard-status-grid .stat-card .value {
        font-size: clamp(18px, 1.5vw, 28px);
        line-height: 1.12;
    }

    .dashboard-chart-stack {
        display: grid;
        grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
        gap: 16px;
    }

    .chart-panel {
        display: grid;
        gap: 12px;
    }

    .chart-panel-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 12px;
        flex-wrap: wrap;
    }

    .chart-panel-header h3 {
        font-size: 18px;
        letter-spacing: -0.03em;
    }

    .filter-row {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 180px));
        gap: 10px;
        align-items: center;
    }

    .chart-shell {
        background:
            linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,247,249,0.98)),
            radial-gradient(circle at top left, rgba(24, 24, 27, 0.045), transparent 38%);
        border: 1px solid var(--line);
        border-radius: 16px;
        padding: 14px 16px 16px;
        box-shadow: inset 0 1px 0 rgba(255,255,255,0.92);
        display: grid;
        gap: 12px;
    }

    .legend-box {
        display: flex;
        flex-wrap: wrap;
        gap: 10px 12px;
    }

    .legend-box.center {
        justify-content: center;
    }

    .legend-item {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        border-radius: 10px;
        padding: 6px 10px;
        background: rgba(255,255,255,0.92);
        border: 1px solid var(--line);
        color: #363b44;
        font-size: 12px;
        font-weight: 700;
    }

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

    .chart-svg {
        width: 100%;
        height: auto;
        aspect-ratio: 1120 / 390;
        min-height: 300px;
        display: block;
    }

    @media (max-width: 1200px) {
        .dashboard-layout {
            grid-template-columns: 1fr;
        }

        .dashboard-chart-stack {
            grid-template-rows: auto;
        }
    }

    @media (max-width: 860px) {
        .filter-row {
            grid-template-columns: 1fr;
        }
    }
</style>
{% endblock %}

{% block content %}
<section class="panel">
    <div class="dashboard-topbar">
        <div class="section-title" style="margin-bottom: 0;">
            <h2>사업현황</h2>
        </div>
        <div class="dashboard-topbar-actions">
            <form method="get" action="/" id="dashboardYearForm">
                <select id="dashboardYearSelect" class="dashboard-year-select" name="overview_year" aria-label="사업현황 연도 선택">
                    <option value="" {% if not overview_selected_year %}selected{% endif %}>최근 10개년</option>
                    {% for year in available_years %}
                        <option value="{{ year }}" {% if overview_selected_year == year %}selected{% endif %}>{{ year }}</option>
                    {% endfor %}
                </select>
            </form>
            <div class="upload-actions">
                <form action="/upload" method="post" enctype="multipart/form-data" id="uploadForm">
                    <input id="excel_file" class="hidden-file-input" type="file" name="excel_file" accept=".xlsx,.xlsm,.xltx,.xltm" required>
                    <button type="button" id="uploadButton" class="button-icon" title="업로드 저장" aria-label="업로드 저장">
                        <svg viewBox="0 0 24 24" aria-hidden="true">
                            <path d="M6 4h9l3 3v13H6z"></path>
                            <path d="M9 4v6h6V4"></path>
                            <path d="M9 17h6"></path>
                        </svg>
                        <span class="sr-only">업로드 저장</span>
                    </button>
                </form>
                <div class="upload-tooltip">엑셀 파일을 선택하면 회계 데이터를 DB에 바로 저장합니다. 프로젝트 폴더에 둔 파일 외에 추가 파일을 수동 반영할 때 사용하세요.</div>
            </div>
        </div>
    </div>

    <div class="dashboard-layout">
        <section class="dashboard-status-panel">
            <div class="dashboard-status-grid">
                <div class="stat-card">
                    <div class="label">수행 프로젝트</div>
                    <div class="value">{{ ((project_dashboard.related_projects or 0) - (project_dashboard.completed_projects or 0)) if ((project_dashboard.related_projects or 0) - (project_dashboard.completed_projects or 0)) > 0 else 0 }}</div>
                </div>
                <div class="stat-card">
                    <div class="label">종료 프로젝트</div>
                    <div class="value">{{ project_dashboard.completed_projects or 0 }}</div>
                </div>
                <div class="stat-card">
                    <div class="label">수금액</div>
                    <div class="value">{{ "{:,.0f}".format(project_dashboard.collection_amount or 0) }}</div>
                </div>
                <div class="stat-card">
                    <div class="label">비용</div>
                    <div class="value">{{ "{:,.0f}".format((overview.total_cost or 0) + (overview.total_sga or 0)) }}</div>
                </div>
                <div class="stat-card">
                    <div class="label">원가</div>
                    <div class="value">{{ "{:,.0f}".format(overview.total_cost or 0) }}</div>
                </div>
                <div class="stat-card">
                    <div class="label">판관비</div>
                    <div class="value">{{ "{:,.0f}".format(overview.total_sga or 0) }}</div>
                </div>
            </div>
        </section>

        <div class="dashboard-chart-stack">
            <section class="chart-panel">
                <div class="chart-panel-header">
                    <h3>수금 구성</h3>
                    <div class="filter-row">
                        <select id="revenueGranularity" aria-label="수금 구성 집계 단위">
                            <option value="yearly" {% if not overview_selected_year %}selected{% endif %}>연도별</option>
                            <option value="monthly" {% if overview_selected_year %}selected{% endif %}>월별</option>
                        </select>
                        <select id="revenueYear" aria-label="수금 구성 연도 선택">
                            <option value="all">전체연도</option>
                            {% for year in available_years %}
                                <option value="{{ year }}" {% if overview_selected_year == year %}selected{% endif %}>{{ year }}</option>
                            {% endfor %}
                        </select>
                        <select id="revenueMetric" aria-label="수금 구성 항목 선택">
                            <option value="all">전체 항목</option>
                            {% for option in dashboard_revenue_metric_options %}
                                <option value="{{ option.item_key }}">{{ option.label }}</option>
                            {% endfor %}
                        </select>
                    </div>
                </div>
                <div class="chart-shell">
                    <div class="legend-box center" id="revenueLegend"></div>
                    <svg id="revenueChart" class="chart-svg" viewBox="0 0 1120 390" preserveAspectRatio="xMidYMid meet"></svg>
                </div>
            </section>

            <section class="chart-panel">
                <div class="chart-panel-header">
                    <h3>지출 구성</h3>
                    <div class="filter-row">
                        <select id="expenseGranularity" aria-label="지출 구성 집계 단위">
                            <option value="yearly" {% if not overview_selected_year %}selected{% endif %}>연도별</option>
                            <option value="monthly" {% if overview_selected_year %}selected{% endif %}>월별</option>
                        </select>
                        <select id="expenseYear" aria-label="지출 구성 연도 선택">
                            <option value="all">전체연도</option>
                            {% for year in available_years %}
                                <option value="{{ year }}" {% if overview_selected_year == year %}selected{% endif %}>{{ year }}</option>
                            {% endfor %}
                        </select>
                        <select id="expenseMetric" aria-label="지출 구성 항목 선택">
                            <option value="all">전체 항목</option>
                            {% for option in dashboard_expense_metric_options %}
                                <option value="{{ option.item_key }}">{{ option.label }}</option>
                            {% endfor %}
                        </select>
                    </div>
                </div>
                <div class="chart-shell">
                    <div class="legend-box center" id="expenseLegend"></div>
                    <svg id="expenseChart" class="chart-svg" viewBox="0 0 1120 390" preserveAspectRatio="xMidYMid meet"></svg>
                </div>
            </section>
        </div>
    </div>
</section>
{% endblock %}

{% block script %}
<script>
    const availableYears = {{ available_years | tojson }};
    const yearlySummary = {{ yearly_summary | tojson }};
    const monthlySummary = {{ monthly_summary | tojson }};
    const revenueYearly = {{ project_revenue_mix_yearly | tojson }};
    const revenueMonthly = {{ project_revenue_mix_monthly | tojson }};
    const pageSelectedYear = {{ overview_selected_year | tojson }};
    const dashboardRevenueMetricOptions = {{ dashboard_revenue_metric_options | tojson }};
    const dashboardExpenseMetricOptions = {{ dashboard_expense_metric_options | tojson }};

    const revenuePalette = Object.fromEntries(
        dashboardRevenueMetricOptions.map((option) => [
            option.item_key,
            { label: option.label, color: option.value },
        ]),
    );

    const expensePalette = Object.fromEntries(
        dashboardExpenseMetricOptions.map((option) => [
            option.item_key,
            { label: option.label, color: option.value },
        ]),
    );

    const revenueMetricMap = {
        all: dashboardRevenueMetricOptions.map((option) => option.item_key),
        ...Object.fromEntries(dashboardRevenueMetricOptions.map((option) => [option.item_key, [option.item_key]])),
    };

    const expenseMetricMap = {
        all: dashboardExpenseMetricOptions.map((option) => option.item_key),
        ...Object.fromEntries(dashboardExpenseMetricOptions.map((option) => [option.item_key, [option.item_key]])),
    };

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

    function formatValueLabel(value) {
        const numeric = Number(value || 0);
        if (!numeric) return "0.0";
        if (numeric >= 100000000) return `${(numeric / 100000000).toFixed(1)}억`;
        if (numeric >= 1000000) return `${(numeric / 1000000).toFixed(1)}백만`;
        if (numeric >= 1000) return `${(numeric / 1000).toFixed(1)}천`;
        return numeric.toFixed(1);
    }

    function formatAxisLabel(value) {
        const numeric = Number(value || 0);
        if (!numeric) return "0";
        if (numeric >= 100000000) return `${numeric / 100000000}억`;
        if (numeric >= 1000000) return `${numeric / 1000000}백만`;
        if (numeric >= 1000) return `${numeric / 1000}천`;
        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 buildPositiveAxis(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 setLegend(containerId, metricKeys, paletteMap) {
        const target = document.getElementById(containerId);
        if (!target) return;
        target.innerHTML = metricKeys.map((key) => `
            <span class="legend-item">
                <span class="legend-swatch" style="background:${paletteMap[key].color}"></span>
                ${paletteMap[key].label}
            </span>
        `).join("");
    }

    function renderEmptyChart(svgId, message) {
        const svg = document.getElementById(svgId);
        if (!svg) return;
        svg.innerHTML = `
            <rect x="0" y="0" width="1120" height="390" rx="8" fill="#f7fafb" stroke="#d6e2e8"></rect>
            <text x="560" y="195" text-anchor="middle" fill="#667887" font-size="18" font-weight="700">${message}</text>
        `;
    }

    function renderGroupedBarChart(svgId, rows, metricKeys, paletteMap, options = {}) {
        const svg = document.getElementById(svgId);
        if (!svg) return;
        if (!rows.length || !metricKeys.length) {
            renderEmptyChart(svgId, "표시할 집계 데이터가 없습니다.");
            return;
        }

        const granularity = options.granularity || "yearly";
        const width = 1120;
        const height = 390;
        const margin = { top: 34, right: 26, bottom: 74, left: 94 };
        const plotWidth = width - margin.left - margin.right;
        const plotHeight = height - margin.top - margin.bottom;
        const maxValue = Math.max(1, ...rows.flatMap((row) => metricKeys.map((key) => Number(row[key] || 0))));
        const { tickStep, tickMax } = buildPositiveAxis(maxValue, 4);
        const groupWidth = plotWidth / Math.max(rows.length, 1);
        const axisBaseY = height - margin.bottom;
        const groupGapRatio = granularity === "monthly" ? 0.28 : 0.18;
        const innerGap = granularity === "monthly" ? 7 : 10;
        const groupInset = Math.max(groupWidth * groupGapRatio, granularity === "monthly" ? 8 : 12);
        const usableGroupWidth = Math.max(groupWidth - groupInset * 2, metricKeys.length * 12);
        const barWidth = Math.min((usableGroupWidth - innerGap * Math.max(metricKeys.length - 1, 0)) / Math.max(metricKeys.length, 1), granularity === "monthly" ? 18 : 34);
        const actualGroupWidth = barWidth * metricKeys.length + innerGap * Math.max(metricKeys.length - 1, 0);
        const groupStartOffset = (groupWidth - actualGroupWidth) / 2;
        const xLabelStep = granularity === "monthly" ? 1 : Math.max(1, Math.ceil(rows.length / 10));

        let markup = `
            <defs>
                <linearGradient id="${svgId}Bg" x1="0" x2="1" y1="0" y2="1">
                    <stop offset="0%" stop-color="#fcfefe" />
                    <stop offset="100%" stop-color="#edf4f7" />
                </linearGradient>
                <filter id="${svgId}Shadow" x="-20%" y="-20%" width="140%" height="160%">
                    <feDropShadow dx="0" dy="8" stdDeviation="8" flood-color="rgba(44, 68, 89, 0.14)" />
                </filter>
            </defs>
            <rect x="0" y="0" width="${width}" height="${height}" rx="8" fill="url(#${svgId}Bg)"></rect>
            <rect x="${margin.left}" y="${margin.top}" width="${plotWidth}" height="${plotHeight}" rx="4" fill="rgba(255,255,255,0.72)" stroke="#dde7ec"></rect>
        `;

        for (let value = 0; value <= tickMax; value += tickStep) {
            const y = margin.top + plotHeight - (value / tickMax) * plotHeight;
            markup += `<line x1="${margin.left}" y1="${y}" x2="${width - margin.right}" y2="${y}" stroke="#d8e3e8" stroke-dasharray="3 7"></line>`;
            markup += `<text x="${margin.left - 16}" y="${y + 4}" text-anchor="end" fill="#60717d" font-size="11.5" font-weight="700">${formatAxisLabel(value)}</text>`;
        }
        markup += `<line x1="${margin.left}" y1="${axisBaseY}" x2="${width - margin.right}" y2="${axisBaseY}" stroke="#8ea0ac" stroke-width="1.2"></line>`;

        rows.forEach((row, rowIndex) => {
            const baseX = margin.left + rowIndex * groupWidth;
            const showXAxisLabel = rows.length <= 14 || rowIndex % xLabelStep === 0 || rowIndex === rows.length - 1;
            metricKeys.forEach((key, metricIndex) => {
                const value = Number(row[key] || 0);
                const x = baseX + groupStartOffset + metricIndex * (barWidth + innerGap);
                const drawWidth = Math.max(barWidth, 10);
                const barHeight = tickMax ? (value / tickMax) * plotHeight : 0;
                const y = margin.top + plotHeight - barHeight;
                const labelY = Math.max(y - 12, margin.top - 8);
                const showValueLabel = barHeight > 24 && (granularity === "yearly" || metricKeys.length <= 2 || drawWidth >= 16);
                markup += `<rect x="${x}" y="${y}" width="${drawWidth}" height="${barHeight}" rx="3" fill="${paletteMap[key].color}" filter="url(#${svgId}Shadow)"></rect>`;
                if (showValueLabel) {
                    markup += `<text x="${x + drawWidth / 2}" y="${labelY}" text-anchor="middle" fill="#35505f" font-size="10" font-weight="700">${formatValueLabel(value)}</text>`;
                }
            });
            if (showXAxisLabel) {
                markup += `<text x="${baseX + groupWidth / 2}" y="${height - 28}" text-anchor="middle" fill="#405362" font-size="12.5" font-weight="700">${row.label}</text>`;
            }
        });

        svg.innerHTML = markup;
    }

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

    function syncYearSelection(selectId, granularity) {
        const select = document.getElementById(selectId);
        if (!select) return;
        if (granularity === "yearly") {
            select.value = "all";
            select.disabled = true;
            return;
        }
        const hasSelectedYear = availableYears.some((year) => String(year) === String(select.value));
        if (select.value === "all" || !hasSelectedYear) {
            select.value = pageSelectedYear ? String(pageSelectedYear) : getLatestAvailableYear();
        }
        select.disabled = false;
    }

    function getRevenueRows() {
        const granularity = document.getElementById("revenueGranularity").value;
        const yearFilter = document.getElementById("revenueYear").value;
        const source = granularity === "yearly" ? revenueYearly : revenueMonthly;
        return source
            .filter((row) => {
                if (granularity === "yearly") return true;
                return yearFilter === "all" ? true : String(row.year) === yearFilter;
            })
            .map((row) => ({
                ...row,
                label: granularity === "yearly" ? String(row.year) : `${row.month}월`,
            }))
            .slice(granularity === "yearly" ? -10 : 0);
    }

    function getExpenseRows() {
        const granularity = document.getElementById("expenseGranularity").value;
        const yearFilter = document.getElementById("expenseYear").value;
        const source = granularity === "yearly" ? yearlySummary : monthlySummary;
        return source
            .filter((row) => {
                if (granularity === "yearly") return true;
                return yearFilter === "all" ? true : String(row.year) === yearFilter;
            })
            .map((row) => ({
                ...row,
                label: granularity === "yearly" ? String(row.year) : `${row.month}월`,
            }))
            .slice(granularity === "yearly" ? -10 : 0);
    }

    function updateRevenueChart() {
        const granularity = document.getElementById("revenueGranularity").value;
        syncYearSelection("revenueYear", granularity);
        const metricKeys = revenueMetricMap[document.getElementById("revenueMetric").value];
        setLegend("revenueLegend", metricKeys, revenuePalette);
        renderGroupedBarChart("revenueChart", getRevenueRows(), metricKeys, revenuePalette, { granularity });
    }

    function updateExpenseChart() {
        const granularity = document.getElementById("expenseGranularity").value;
        syncYearSelection("expenseYear", granularity);
        const metricKeys = expenseMetricMap[document.getElementById("expenseMetric").value];
        setLegend("expenseLegend", metricKeys, expensePalette);
        renderGroupedBarChart("expenseChart", getExpenseRows(), metricKeys, expensePalette, { granularity });
    }

    document.getElementById("revenueGranularity")?.addEventListener("change", updateRevenueChart);
    document.getElementById("revenueYear")?.addEventListener("change", updateRevenueChart);
    document.getElementById("revenueMetric")?.addEventListener("change", updateRevenueChart);
    document.getElementById("expenseGranularity")?.addEventListener("change", updateExpenseChart);
    document.getElementById("expenseYear")?.addEventListener("change", updateExpenseChart);
    document.getElementById("expenseMetric")?.addEventListener("change", updateExpenseChart);

    document.getElementById("dashboardYearSelect")?.addEventListener("change", (event) => {
        event.target.form?.submit();
    });

    const uploadButton = document.getElementById("uploadButton");
    const excelInput = document.getElementById("excel_file");
    const uploadForm = document.getElementById("uploadForm");

    uploadButton?.addEventListener("click", () => {
        excelInput?.click();
    });

    excelInput?.addEventListener("change", () => {
        if (excelInput.files && excelInput.files.length > 0) {
            uploadForm.submit();
        }
    });

    syncYearSelection("revenueYear", document.getElementById("revenueGranularity")?.value || "yearly");
    syncYearSelection("expenseYear", document.getElementById("expenseGranularity")?.value || "yearly");
    updateRevenueChart();
    updateExpenseChart();
</script>
{% endblock %}
