"""PDF 리포트 생성기"""
from pathlib import Path
from io import BytesIO

from reportlab.lib.pagesizes import A4, landscape
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    Image, PageBreak, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

from ..data_models import FinancialReport, PLItem, KPICard
from .styles import (
    COLORS, FONT_SIZES, MARGINS, TABLE_STYLES,
    format_currency, get_font_path
)


class ReportGenerator:
    """PDF 리포트 생성기"""

    def __init__(self, output_path: Path, report_data: FinancialReport):
        self.output_path = Path(output_path)
        self.data = report_data
        self.total_pages = 0

        # 폰트 등록
        self._register_fonts()

        # 스타일 초기화
        self.styles = getSampleStyleSheet()
        self._setup_styles()

    def _register_fonts(self):
        """일본어 폰트 등록"""
        try:
            font_path = get_font_path("gothic")
            if font_path != "Helvetica":
                pdfmetrics.registerFont(TTFont("Japanese", font_path))
                self.font_name = "Japanese"
            else:
                self.font_name = "Helvetica"
        except Exception:
            self.font_name = "Helvetica"

    def _setup_styles(self):
        """커스텀 스타일 설정"""
        self.styles.add(ParagraphStyle(
            name="PageTitle",
            fontName=self.font_name,
            fontSize=FONT_SIZES["title"],
            textColor=COLORS["dark_gray"],
            spaceAfter=20,
        ))

        self.styles.add(ParagraphStyle(
            name="SectionTitle",
            fontName=self.font_name,
            fontSize=FONT_SIZES["heading1"],
            textColor=COLORS["primary"],
            spaceBefore=15,
            spaceAfter=10,
        ))

        # BodyText is already defined, so update it instead
        self.styles["BodyText"].fontName = self.font_name
        self.styles["BodyText"].fontSize = FONT_SIZES["body"]
        self.styles["BodyText"].textColor = COLORS["dark_gray"]

        self.styles.add(ParagraphStyle(
            name="SmallText",
            fontName=self.font_name,
            fontSize=FONT_SIZES["small"],
            textColor=COLORS["secondary"],
        ))

    def generate(self):
        """PDF 생성"""
        doc = SimpleDocTemplate(
            str(self.output_path),
            pagesize=landscape(A4),
            topMargin=MARGINS["page_top"],
            bottomMargin=MARGINS["page_bottom"],
            leftMargin=MARGINS["page_left"],
            rightMargin=MARGINS["page_right"],
        )

        story = []

        # 1. 표지/대시보드 페이지
        story.extend(self._create_dashboard_page())

        # 2. 요약 페이지
        story.append(PageBreak())
        story.extend(self._create_summary_page())

        # 3. P/L 테이블 페이지
        story.append(PageBreak())
        story.extend(self._create_pl_table_page())

        # 4. 차트 페이지
        if self.data.charts:
            story.append(PageBreak())
            story.extend(self._create_charts_page())

        # 문서 빌드
        doc.build(story, onFirstPage=self._add_header_footer,
                  onLaterPages=self._add_header_footer)

        # 페이지 수 계산
        self.total_pages = doc.page

    def _add_header_footer(self, canvas, doc):
        """헤더/푸터 추가"""
        canvas.saveState()

        # 헤더
        canvas.setFont(self.font_name, FONT_SIZES["small"])
        canvas.setFillColor(COLORS["secondary"])
        canvas.drawString(
            MARGINS["page_left"],
            doc.height + MARGINS["page_top"] + 15,
            f"{self.data.company_name} - {self.data.fiscal_year}"
        )

        # 푸터
        canvas.drawRightString(
            doc.width + MARGINS["page_left"],
            15,
            f"Generated: {self.data.generated_at.strftime('%Y-%m-%d')} | Page {doc.page}"
        )

        canvas.restoreState()

    def _create_dashboard_page(self) -> list:
        """대시보드 페이지 생성"""
        elements = []

        # 제목
        elements.append(Paragraph(
            f"業績見通し - {self.data.target_month}",
            self.styles["PageTitle"]
        ))

        # KPI 카드들
        if self.data.annual_kpis:
            kpi_table = self._create_kpi_cards_table(self.data.annual_kpis)
            elements.append(kpi_table)
            elements.append(Spacer(1, 20))

        # 설명 텍스트
        elements.append(Paragraph(
            f"対象期間: {self.data.period}",
            self.styles["SmallText"]
        ))

        return elements

    def _create_kpi_cards_table(self, kpis: list[KPICard]) -> Table:
        """KPI 카드 테이블 생성"""
        # 한 행에 4개씩 배치
        cards_per_row = 4
        rows = []
        current_row = []

        for kpi in kpis:
            card_content = self._create_kpi_card_content(kpi)
            current_row.append(card_content)

            if len(current_row) == cards_per_row:
                rows.append(current_row)
                current_row = []

        if current_row:
            # 빈 셀로 채우기
            while len(current_row) < cards_per_row:
                current_row.append("")
            rows.append(current_row)

        if not rows:
            return Table([[""]])

        table = Table(rows, colWidths=[180] * cards_per_row)
        table.setStyle(TableStyle([
            ("VALIGN", (0, 0), (-1, -1), "TOP"),
            ("LEFTPADDING", (0, 0), (-1, -1), 5),
            ("RIGHTPADDING", (0, 0), (-1, -1), 5),
            ("TOPPADDING", (0, 0), (-1, -1), 5),
            ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
        ]))

        return table

    def _create_kpi_card_content(self, kpi: KPICard) -> Table:
        """개별 KPI 카드 내용 생성"""
        title_style = ParagraphStyle(
            "KPITitle",
            fontName=self.font_name,
            fontSize=FONT_SIZES["small"],
            textColor=COLORS["secondary"],
        )

        value_style = ParagraphStyle(
            "KPIValue",
            fontName=self.font_name,
            fontSize=FONT_SIZES["heading1"],
            textColor=colors.HexColor(kpi.color),
        )

        data = [
            [Paragraph(kpi.title, title_style)],
            [Paragraph(kpi.formatted_value, value_style)],
        ]

        # 목표 대비 달성률 추가
        if kpi.target_value and kpi.target_value != 0:
            rate = (kpi.value / kpi.target_value) * 100
            rate_text = f"達成率: {rate:.1f}%"
            rate_style = ParagraphStyle(
                "KPIRate",
                fontName=self.font_name,
                fontSize=FONT_SIZES["tiny"],
                textColor=COLORS["success"] if rate >= 100 else COLORS["danger"],
            )
            data.append([Paragraph(rate_text, rate_style)])

        card = Table(data, colWidths=[160])
        card.setStyle(TableStyle([
            ("BACKGROUND", (0, 0), (-1, -1), COLORS["light_gray"]),
            ("BOX", (0, 0), (-1, -1), 1, COLORS["border"]),
            ("LEFTPADDING", (0, 0), (-1, -1), 10),
            ("RIGHTPADDING", (0, 0), (-1, -1), 10),
            ("TOPPADDING", (0, 0), (-1, -1), 8),
            ("BOTTOMPADDING", (0, 0), (-1, -1), 8),
        ]))

        return card

    def _create_summary_page(self) -> list:
        """요약 페이지 생성"""
        elements = []

        elements.append(Paragraph("サマリ", self.styles["PageTitle"]))

        # 주요 지표 요약 테이블
        if self.data.pl_items:
            summary_items = [
                item for item in self.data.pl_items
                if any(kw in item.name for kw in ["売上高", "売上原価", "売上総利益", "営業利益", "経常利益"])
            ]

            if summary_items:
                table = self._create_summary_table(summary_items)
                elements.append(table)

        return elements

    def _create_summary_table(self, items: list[PLItem]) -> Table:
        """요약 테이블 생성"""
        header = ["科目", "予算(累計)", "実績(累計)", "差異", "達成率"]
        data = [header]

        for item in items:
            budget = item.total_budget
            actual = item.total_actual
            variance = item.total_variance
            rate = item.total_achievement_rate

            row = [
                item.name,
                format_currency(budget),
                format_currency(actual),
                format_currency(variance),
                f"{rate:.1f}%" if rate else "-",
            ]
            data.append(row)

        table = Table(data, colWidths=[120, 100, 100, 80, 60])
        table.setStyle(TableStyle([
            # 헤더 스타일
            ("BACKGROUND", (0, 0), (-1, 0), TABLE_STYLES["header_bg"]),
            ("TEXTCOLOR", (0, 0), (-1, 0), TABLE_STYLES["header_text"]),
            ("FONTNAME", (0, 0), (-1, -1), self.font_name),
            ("FONTSIZE", (0, 0), (-1, 0), FONT_SIZES["small"]),
            ("FONTSIZE", (0, 1), (-1, -1), FONT_SIZES["body"]),
            # 정렬
            ("ALIGN", (1, 0), (-1, -1), "RIGHT"),
            ("ALIGN", (0, 0), (0, -1), "LEFT"),
            # 테두리
            ("GRID", (0, 0), (-1, -1), 0.5, COLORS["border"]),
            # 패딩
            ("LEFTPADDING", (0, 0), (-1, -1), 8),
            ("RIGHTPADDING", (0, 0), (-1, -1), 8),
            ("TOPPADDING", (0, 0), (-1, -1), 6),
            ("BOTTOMPADDING", (0, 0), (-1, -1), 6),
            # 교대 배경색
            *[("BACKGROUND", (0, i), (-1, i), TABLE_STYLES["alt_row_bg"])
              for i in range(2, len(data), 2)],
        ]))

        return table

    def _create_pl_table_page(self) -> list:
        """P/L 테이블 페이지 생성"""
        elements = []

        elements.append(Paragraph("損益計算書", self.styles["PageTitle"]))

        if not self.data.pl_items:
            elements.append(Paragraph("データがありません", self.styles["BodyText"]))
            return elements

        # 월별 헤더 생성
        month_labels = self.data.month_labels[:6]  # 최대 6개월 표시 (공간 제약)
        header = ["科目"] + month_labels + ["累計"]
        data = [header]

        for item in self.data.pl_items[:30]:  # 최대 30개 항목
            row = [item.name]

            for i, month in enumerate(month_labels):
                if i < len(item.monthly_values):
                    row.append(format_currency(item.monthly_values[i].actual))
                else:
                    row.append("-")

            row.append(format_currency(item.total_actual))
            data.append(row)

        # 컬럼 폭 계산
        col_widths = [100] + [70] * len(month_labels) + [80]

        table = Table(data, colWidths=col_widths)
        table.setStyle(TableStyle([
            # 헤더 스타일
            ("BACKGROUND", (0, 0), (-1, 0), TABLE_STYLES["header_bg"]),
            ("TEXTCOLOR", (0, 0), (-1, 0), TABLE_STYLES["header_text"]),
            ("FONTNAME", (0, 0), (-1, -1), self.font_name),
            ("FONTSIZE", (0, 0), (-1, 0), FONT_SIZES["tiny"]),
            ("FONTSIZE", (0, 1), (-1, -1), FONT_SIZES["tiny"]),
            # 정렬
            ("ALIGN", (1, 0), (-1, -1), "RIGHT"),
            ("ALIGN", (0, 0), (0, -1), "LEFT"),
            # 테두리
            ("GRID", (0, 0), (-1, -1), 0.5, COLORS["border"]),
            # 패딩
            ("LEFTPADDING", (0, 0), (-1, -1), 4),
            ("RIGHTPADDING", (0, 0), (-1, -1), 4),
            ("TOPPADDING", (0, 0), (-1, -1), 3),
            ("BOTTOMPADDING", (0, 0), (-1, -1), 3),
        ]))

        elements.append(table)

        return elements

    def _create_charts_page(self) -> list:
        """차트 페이지 생성"""
        elements = []

        elements.append(Paragraph("業績分析", self.styles["PageTitle"]))

        # 차트 이미지 생성 및 삽입
        from ..charts import create_combo_chart, create_line_chart

        for chart_key, chart_data in self.data.charts.items():
            elements.append(Paragraph(chart_data.title, self.styles["SectionTitle"]))

            try:
                if chart_data.chart_type == "combo":
                    img_buffer = create_combo_chart(chart_data)
                elif chart_data.chart_type == "line":
                    img_buffer = create_line_chart(chart_data)
                else:
                    continue

                if img_buffer:
                    img = Image(img_buffer, width=400, height=200)
                    elements.append(img)
                    elements.append(Spacer(1, 20))
            except Exception as e:
                elements.append(Paragraph(f"チャート生成エラー: {e}", self.styles["SmallText"]))

        return elements
