"""
月次レポート生成サービス - PDF生成
"""
import os
import sys
from datetime import datetime

from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.lib.colors import HexColor, black, white
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle,
    PageBreak, KeepTogether
)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.charts import ChartGenerator


class MonthlyReportPDF:
    """月次レポートPDF生成クラス"""

    # 色定義
    PRIMARY_COLOR = HexColor('#2196F3')
    SUCCESS_COLOR = HexColor('#4CAF50')
    DANGER_COLOR = HexColor('#F44336')
    WARNING_COLOR = HexColor('#FF9800')
    GRAY_COLOR = HexColor('#9E9E9E')
    LIGHT_GRAY = HexColor('#F5F5F5')
    ANALYSIS_BG_COLOR = HexColor('#E3F2FD')  # 薄い青（AI分析ボックス用）

    def __init__(self, data: dict, chart_generator: ChartGenerator = None, analyses: dict = None):
        self.data = data
        self.charts = chart_generator or ChartGenerator()
        self.analyses = analyses or {}
        self.elements = []
        self.styles = getSampleStyleSheet()
        self._setup_fonts()
        self._setup_styles()

    def _setup_fonts(self):
        """日本語フォント登録"""
        from reportlab.pdfbase.cidfonts import UnicodeCIDFont

        self.font_name = 'Helvetica'  # デフォルト

        # 方法1: CIDフォント（組み込み日本語フォント）を試す
        try:
            pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))
            self.font_name = 'HeiseiKakuGo-W5'
            return
        except Exception:
            pass

        # 方法2: TTFファイルを直接指定
        # Windows/Mac/Linuxで利用可能なTTFフォントファイル（TTCではない）
        font_paths = [
            # Windows - 単独TTFファイル
            ('C:/Windows/Fonts/GOTHIC.TTF', None),      # MS Gothic
            ('C:/Windows/Fonts/MSGOTHIC.TTC', 0),       # MS Gothic (TTC)
            ('C:/Windows/Fonts/meiryo.ttc', 0),         # Meiryo (TTC)
            ('C:/Windows/Fonts/YuGothR.ttc', 0),        # Yu Gothic (TTC)
            # Mac
            ('/Library/Fonts/Arial Unicode.ttf', None),
            # Linux
            ('/usr/share/fonts/truetype/fonts-japanese-gothic.ttf', None),
        ]

        for font_info in font_paths:
            font_path = font_info[0]
            subfont_index = font_info[1] if len(font_info) > 1 else None

            if os.path.exists(font_path):
                try:
                    if subfont_index is not None:
                        # TTCファイルの場合
                        pdfmetrics.registerFont(TTFont('JapaneseFont', font_path, subfontIndex=subfont_index))
                    else:
                        # TTFファイルの場合
                        pdfmetrics.registerFont(TTFont('JapaneseFont', font_path))
                    self.font_name = 'JapaneseFont'
                    return
                except Exception as e:
                    continue

    def _setup_styles(self):
        """スタイル設定"""
        # タイトルスタイル
        self.styles.add(ParagraphStyle(
            name='JTitle',
            fontName=self.font_name,
            fontSize=24,
            leading=30,
            alignment=TA_CENTER,
            spaceAfter=20,
        ))

        # 見出しスタイル
        self.styles.add(ParagraphStyle(
            name='JHeading1',
            fontName=self.font_name,
            fontSize=16,
            leading=20,
            textColor=self.PRIMARY_COLOR,
            spaceBefore=15,
            spaceAfter=10,
        ))

        self.styles.add(ParagraphStyle(
            name='JHeading2',
            fontName=self.font_name,
            fontSize=12,
            leading=16,
            textColor=black,
            spaceBefore=10,
            spaceAfter=5,
        ))

        # 本文スタイル
        self.styles.add(ParagraphStyle(
            name='JBody',
            fontName=self.font_name,
            fontSize=10,
            leading=14,
            alignment=TA_LEFT,
        ))

        # 小さいテキスト
        self.styles.add(ParagraphStyle(
            name='JSmall',
            fontName=self.font_name,
            fontSize=8,
            leading=10,
            textColor=self.GRAY_COLOR,
        ))

        # 中央揃え
        self.styles.add(ParagraphStyle(
            name='JCenter',
            fontName=self.font_name,
            fontSize=10,
            leading=14,
            alignment=TA_CENTER,
        ))

        # テーブルセル用スタイル
        self.styles.add(ParagraphStyle(
            name='JTableCell',
            fontName=self.font_name,
            fontSize=10,
            leading=12,
            alignment=TA_LEFT,
            wordWrap='CJK',
        ))

        self.styles.add(ParagraphStyle(
            name='JTableCellRight',
            fontName=self.font_name,
            fontSize=10,
            leading=12,
            alignment=TA_RIGHT,
            wordWrap='CJK',
        ))

        self.styles.add(ParagraphStyle(
            name='JTableCellSmall',
            fontName=self.font_name,
            fontSize=9,
            leading=11,
            alignment=TA_LEFT,
            wordWrap='CJK',
        ))

        self.styles.add(ParagraphStyle(
            name='JTableHeader',
            fontName=self.font_name,
            fontSize=10,
            leading=12,
            alignment=TA_CENTER,
            textColor=white,
        ))

        # AI分析コメント用スタイル
        self.styles.add(ParagraphStyle(
            name='JAnalysis',
            fontName=self.font_name,
            fontSize=9,
            leading=13,
            alignment=TA_LEFT,
            textColor=HexColor('#1565C0'),  # 濃い青のテキスト
        ))

        self.styles.add(ParagraphStyle(
            name='JAnalysisLabel',
            fontName=self.font_name,
            fontSize=8,
            leading=10,
            alignment=TA_LEFT,
            textColor=HexColor('#1976D2'),
        ))

    def _p(self, text, style_name='JTableCell'):
        """テーブルセル用のParagraphを生成"""
        return Paragraph(str(text), self.styles[style_name])

    def _img(self, path, max_width=170*mm, max_height=None):
        """アスペクト比を維持した画像を生成"""
        if not path or not os.path.exists(path):
            return None

        # 画像の実際のサイズを取得
        from reportlab.lib.utils import ImageReader
        img_reader = ImageReader(path)
        img_width, img_height = img_reader.getSize()

        # アスペクト比を計算
        aspect_ratio = img_width / img_height

        # max_widthに収まるようにサイズを計算
        width = max_width
        height = width / aspect_ratio

        # max_heightが指定されていて超える場合は、高さ基準でリサイズ
        if max_height and height > max_height:
            height = max_height
            width = height * aspect_ratio

        return Image(path, width=width, height=height)

    def _add_analysis_box(self, section: str):
        """AI分析コメントボックスを追加"""
        if section not in self.analyses:
            return

        comment = self.analyses[section]
        if not comment:
            return

        # ラベルとコメントを改行で結合
        label = Paragraph("AI分析:", self.styles['JAnalysisLabel'])
        content = Paragraph(comment.replace('\n', '<br/>'), self.styles['JAnalysis'])

        # テーブルで背景色付きボックスを作成
        box_data = [[label], [content]]
        box_table = Table(box_data, colWidths=[165*mm])
        box_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), self.ANALYSIS_BG_COLOR),
            ('BOX', (0, 0), (-1, -1), 1, HexColor('#90CAF9')),  # 薄い青のボーダー
            ('TOPPADDING', (0, 0), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
            ('LEFTPADDING', (0, 0), (-1, -1), 10),
            ('RIGHTPADDING', (0, 0), (-1, -1), 10),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))

        self.elements.append(Spacer(1, 5*mm))
        self.elements.append(box_table)

    def generate(self, output_path: str) -> str:
        """PDF生成メイン"""
        doc = SimpleDocTemplate(
            output_path,
            pagesize=A4,
            topMargin=15*mm,
            bottomMargin=15*mm,
            leftMargin=15*mm,
            rightMargin=15*mm,
        )

        # 各ページを追加
        self._add_cover_page()
        self._add_summary_page()
        self._add_usage_overview_page()
        self._add_usage_pattern_page()
        self._add_quality_page()
        self._add_keyword_page()
        self._add_improvement_page()
        self._add_glossary_page()

        doc.build(self.elements)
        return output_path

    def _add_cover_page(self):
        """表紙ページ"""
        self.elements.append(Spacer(1, 80*mm))

        period = self.data['period']
        title = f"月次運用レポート"
        self.elements.append(Paragraph(title, self.styles['JTitle']))

        subtitle = f"{period['year']}年{period['month']}月"
        self.elements.append(Paragraph(subtitle, self.styles['JHeading1']))

        self.elements.append(Spacer(1, 40*mm))

        # 生成日時
        generated = f"生成日時: {datetime.now().strftime('%Y年%m月%d日 %H:%M')}"
        self.elements.append(Paragraph(generated, self.styles['JCenter']))

        self.elements.append(PageBreak())

    def _add_summary_page(self):
        """ページ1: サマリー"""
        self.elements.append(Paragraph("1. サマリー", self.styles['JHeading1']))

        summary = self.data['summary']
        comparison = self.data['comparison']
        savings = self.data['estimated_savings']

        # サマリーカード風のテーブル
        card_data = [
            [self._p('総会話数'), self._p(f"{summary['total_conversations']:,}件", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['total_conversations'])],
            [self._p('ユニークIP数'), self._p(f"{summary['unique_ips']:,}", 'JTableCellRight'), self._p('')],
            [self._p('ユーザーあたり平均会話数'), self._p(f"{summary['conversations_per_user']}回", 'JTableCellRight'), self._p('')],
            [self._p('自動解決率'), self._p(f"{summary['resolution_rate']}%", 'JTableCellRight'), self._p('')],
            [self._p('未解決率'), self._p(f"{summary['unresolved_rate']}%", 'JTableCellRight'), self._p('')],
            [self._p('満足度'), self._p(f"{summary['satisfaction_rate']}%", 'JTableCellRight'), self._p('')],
        ]

        card_table = Table(card_data, colWidths=[60*mm, 50*mm, 40*mm])
        card_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), self.LIGHT_GRAY),
            ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
            ('TOPPADDING', (0, 0), (-1, -1), 8),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ]))
        self.elements.append(card_table)
        self.elements.append(Spacer(1, 10*mm))

        # 推定削減効果
        self.elements.append(Paragraph("推定削減効果", self.styles['JHeading2']))

        savings_text = f"""
        自動回答できた質問: {savings['answered_count']:,}件<br/>
        推定削減時間: {savings['saved_hours']}時間 ({savings['saved_minutes']:,}分)<br/>
        推定削減コスト: ¥{savings['saved_cost']:,}<br/>
        <font size=8 color="gray">※ 1件あたり{savings['estimated_handle_time_minutes']}分、時給{savings['hourly_cost']:,}円で計算</font>
        """
        self.elements.append(Paragraph(savings_text, self.styles['JBody']))

        self.elements.append(Spacer(1, 10*mm))

        # 前月比較
        self.elements.append(Paragraph("前月比較", self.styles['JHeading2']))

        comp_data = [
            [self._p('指標', 'JTableHeader'), self._p('前月', 'JTableHeader'),
             self._p('当月', 'JTableHeader'), self._p('増減', 'JTableHeader')],
            [self._p('総会話数'),
             self._p(f"{comparison['previous']['total_conversations']:,}", 'JTableCellRight'),
             self._p(f"{comparison['current']['total_conversations']:,}", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['total_conversations'])],
            [self._p('回答できた'),
             self._p(f"{comparison['previous']['answered_count']:,}", 'JTableCellRight'),
             self._p(f"{comparison['current']['answered_count']:,}", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['answered_count'])],
            [self._p('回答できなかった'),
             self._p(f"{comparison['previous']['unanswered_count']:,}", 'JTableCellRight'),
             self._p(f"{comparison['current']['unanswered_count']:,}", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['unanswered_count'], inverse=True)],
            [self._p('いいね'),
             self._p(f"{comparison['previous']['likes_count']:,}", 'JTableCellRight'),
             self._p(f"{comparison['current']['likes_count']:,}", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['likes_count'])],
            [self._p('よくない'),
             self._p(f"{comparison['previous']['dislikes_count']:,}", 'JTableCellRight'),
             self._p(f"{comparison['current']['dislikes_count']:,}", 'JTableCellRight'),
             self._format_change_p(comparison['changes']['dislikes_count'], inverse=True)],
        ]

        comp_table = Table(comp_data, colWidths=[45*mm, 35*mm, 35*mm, 35*mm])
        comp_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), self.PRIMARY_COLOR),
            ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
            ('TOPPADDING', (0, 0), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ]))
        self.elements.append(comp_table)

        # AI分析コメント
        self._add_analysis_box('summary')

        self.elements.append(PageBreak())

    def _add_usage_overview_page(self):
        """ページ2: 利用状況（概要）"""
        self.elements.append(Paragraph("2. 利用状況（概要）", self.styles['JHeading1']))

        # 日別会話数グラフ
        daily_chart = self.charts.generate_daily_chart(self.data['daily_stats'])
        if daily_chart and os.path.exists(daily_chart):
            img = self._img(daily_chart, max_width=170*mm)
            if img:
                self.elements.append(img)
        self.elements.append(Spacer(1, 5*mm))

        # 前月比較グラフ
        self.elements.append(Paragraph("前月比較", self.styles['JHeading2']))
        comp_chart = self.charts.generate_comparison_chart(self.data['comparison'])
        if comp_chart and os.path.exists(comp_chart):
            img = self._img(comp_chart, max_width=140*mm)
            if img:
                self.elements.append(img)
        self.elements.append(Spacer(1, 5*mm))

        # 平均会話ターン数
        avg_turns = self.data['avg_turns']
        self.elements.append(Paragraph("セッション統計", self.styles['JHeading2']))
        turns_text = f"""
        総セッション数: {avg_turns['total_sessions']:,}<br/>
        平均会話ターン数: {avg_turns['average']}<br/>
        最大ターン数: {avg_turns['max']}<br/>
        最小ターン数: {avg_turns['min']}
        """
        self.elements.append(Paragraph(turns_text, self.styles['JBody']))

        # AI分析コメント
        self._add_analysis_box('usage_overview')

        self.elements.append(PageBreak())

    def _add_usage_pattern_page(self):
        """ページ3: 利用パターン分析"""
        self.elements.append(Paragraph("3. 利用パターン分析", self.styles['JHeading1']))

        # 時間帯別分布
        hourly_chart = self.charts.generate_hourly_chart(self.data['hourly_stats'])
        if hourly_chart and os.path.exists(hourly_chart):
            img = self._img(hourly_chart, max_width=170*mm)
            if img:
                self.elements.append(img)
        self.elements.append(Spacer(1, 5*mm))

        # 曜日別傾向
        dow_chart = self.charts.generate_day_of_week_chart(self.data['day_of_week_stats'])
        if dow_chart and os.path.exists(dow_chart):
            img = self._img(dow_chart, max_width=140*mm)
            if img:
                self.elements.append(img)
        self.elements.append(Spacer(1, 5*mm))

        # ヒートマップ
        self.elements.append(Paragraph("曜日×時間帯分布", self.styles['JHeading2']))
        heatmap = self.charts.generate_heatmap(self.data['hour_by_day_of_week'])
        if heatmap and os.path.exists(heatmap):
            img = self._img(heatmap, max_width=170*mm)
            if img:
                self.elements.append(img)

        # AI分析コメント
        self._add_analysis_box('usage_pattern')

        self.elements.append(PageBreak())

    def _add_quality_page(self):
        """ページ4: 回答品質"""
        self.elements.append(Paragraph("4. 回答品質", self.styles['JHeading1']))

        summary = self.data['summary']

        # 回答可否の円グラフ
        resolution_pie = self.charts.generate_resolution_pie(
            summary['answered_count'], summary['unanswered_count']
        )
        satisfaction_pie = self.charts.generate_satisfaction_pie(
            summary['likes_count'], summary['dislikes_count']
        )

        # 2つの円グラフを横に並べる
        pie_row = []
        if resolution_pie and os.path.exists(resolution_pie):
            img = self._img(resolution_pie, max_width=75*mm, max_height=75*mm)
            if img:
                pie_row.append(img)
        if satisfaction_pie and os.path.exists(satisfaction_pie):
            img = self._img(satisfaction_pie, max_width=75*mm, max_height=75*mm)
            if img:
                pie_row.append(img)

        if pie_row:
            pie_table = Table([pie_row], colWidths=[85*mm, 85*mm])
            self.elements.append(pie_table)

        self.elements.append(Spacer(1, 5*mm))

        # フィードバック推移
        self.elements.append(Paragraph("フィードバック推移", self.styles['JHeading2']))
        feedback_chart = self.charts.generate_feedback_chart(self.data['feedback_daily_stats'])
        if feedback_chart and os.path.exists(feedback_chart):
            img = self._img(feedback_chart, max_width=170*mm)
            if img:
                self.elements.append(img)

        self.elements.append(Spacer(1, 5*mm))

        # 低評価フィードバック一覧
        self.elements.append(Paragraph("低評価フィードバック（最新）", self.styles['JHeading2']))
        disliked = self.data['recent_disliked']
        if disliked:
            dislike_data = [[
                Paragraph('日時', self.styles['JTableHeader']),
                Paragraph('質問（抜粋）', self.styles['JTableHeader'])
            ]]
            for d in disliked[:5]:
                ts = d['timestamp']
                if hasattr(ts, 'strftime'):
                    ts = ts.strftime('%m/%d %H:%M')
                question = d['question'][:50] + '...' if d['question'] and len(d['question']) > 50 else (d['question'] or '')
                dislike_data.append([
                    self._p(str(ts), 'JTableCellSmall'),
                    self._p(question, 'JTableCellSmall')
                ])

            dislike_table = Table(dislike_data, colWidths=[30*mm, 135*mm])
            dislike_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), self.DANGER_COLOR),
                ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
                ('TOPPADDING', (0, 0), (-1, -1), 4),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
                ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ]))
            self.elements.append(dislike_table)
        else:
            self.elements.append(Paragraph("低評価フィードバックはありませんでした。", self.styles['JBody']))

        # AI分析コメント
        self._add_analysis_box('quality')

        self.elements.append(PageBreak())

    def _add_keyword_page(self):
        """ページ5: キーワード分析"""
        self.elements.append(Paragraph("5. キーワード分析", self.styles['JHeading1']))

        # 頻出キーワード
        keywords_chart = self.charts.generate_keywords_chart(self.data['keywords'])
        if keywords_chart and os.path.exists(keywords_chart):
            img = self._img(keywords_chart, max_width=140*mm)
            if img:
                self.elements.append(img)

        self.elements.append(Spacer(1, 5*mm))

        # 未解決キーワード
        self.elements.append(Paragraph("未解決に多いキーワード", self.styles['JHeading2']))
        unresolved_chart = self.charts.generate_unresolved_keywords_chart(self.data['unresolved_keywords'])
        if unresolved_chart and os.path.exists(unresolved_chart):
            img = self._img(unresolved_chart, max_width=140*mm)
            if img:
                self.elements.append(img)

        # AI分析コメント
        self._add_analysis_box('keywords')

        self.elements.append(PageBreak())

    def _add_improvement_page(self):
        """ページ6: 改善提案"""
        self.elements.append(Paragraph("6. 改善提案", self.styles['JHeading1']))

        # 回答できなかった質問一覧
        self.elements.append(Paragraph("回答できなかった質問一覧", self.styles['JHeading2']))
        unanswered = self.data['unanswered_questions']

        if unanswered:
            # 質問をグルーピング（重複を除去）
            unique_questions = {}
            for q in unanswered:
                question = q['user_message']
                if question not in unique_questions:
                    unique_questions[question] = 1
                else:
                    unique_questions[question] += 1

            # 出現回数順にソート
            sorted_questions = sorted(unique_questions.items(), key=lambda x: x[1], reverse=True)

            q_data = [[
                Paragraph('質問', self.styles['JTableHeader']),
                Paragraph('回数', self.styles['JTableHeader'])
            ]]
            for q, count in sorted_questions[:15]:
                q_data.append([
                    self._p(q, 'JTableCellSmall'),
                    self._p(str(count), 'JTableCellSmall')
                ])

            q_table = Table(q_data, colWidths=[145*mm, 20*mm])
            q_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), self.WARNING_COLOR),
                ('ALIGN', (1, 0), (1, -1), 'CENTER'),
                ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
                ('TOPPADDING', (0, 0), (-1, -1), 4),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
                ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ]))
            self.elements.append(q_table)

            total_unanswered = len(unanswered)
            self.elements.append(Spacer(1, 3*mm))
            self.elements.append(Paragraph(
                f"※ 合計{total_unanswered}件の未回答質問（上位15件を表示）",
                self.styles['JSmall']
            ))
        else:
            self.elements.append(Paragraph("回答できなかった質問はありませんでした。", self.styles['JBody']))

        self.elements.append(Spacer(1, 10*mm))

        # Q&A追加候補（手動記入欄）
        self.elements.append(Paragraph("Q&A追加候補（手動記入欄）", self.styles['JHeading2']))
        memo_data = [
            [self._p('No.', 'JTableHeader'), self._p('質問', 'JTableHeader'), self._p('回答案', 'JTableHeader')],
            [self._p('1'), self._p(''), self._p('')],
            [self._p('2'), self._p(''), self._p('')],
            [self._p('3'), self._p(''), self._p('')],
        ]
        memo_table = Table(memo_data, colWidths=[10*mm, 75*mm, 75*mm])
        memo_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), self.PRIMARY_COLOR),
            ('ALIGN', (0, 0), (0, -1), 'CENTER'),
            ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
            ('TOPPADDING', (0, 0), (-1, -1), 10),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 10),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))
        self.elements.append(memo_table)

        # AI分析コメント
        self._add_analysis_box('improvement')

        self.elements.append(PageBreak())

    def _add_glossary_page(self):
        """ページ7: 指標の見方"""
        self.elements.append(Paragraph("7. 指標の見方", self.styles['JHeading1']))

        definitions = [
            ('総会話数', 'ユーザーからの質問とボットからの回答を1セットとしてカウントした数。'),
            ('ユニークIP数', '期間中にアクセスした異なるIPアドレスの数。ユーザー数の参考値。※同一組織からは同じIPとなる場合があり、モバイル環境ではIPが変動する場合があります。'),
            ('ユーザーあたり平均会話数', '総会話数をユニークIP数で割った値。1ユーザーが平均何回会話したかの目安。'),
            ('自動解決率', '総会話数のうち、ボットが適切な回答を返せた割合。「回答できた数 ÷ 総会話数 × 100」で計算。'),
            ('未解決率', '総会話数のうち、ボットが回答できなかった割合。「回答できなかった数 ÷ 総会話数 × 100」で計算。'),
            ('満足度', 'フィードバックを受けた回答のうち「いいね」の割合。「いいね数 ÷ 総フィードバック数 × 100」で計算。'),
            ('フィードバック率', '総会話数のうちフィードバックが付いた割合。'),
            ('推定削減時間', '自動回答できた質問を人が対応した場合の想定時間。'),
            ('推定削減コスト', '推定削減時間を時給換算した金額。'),
            ('セッション', 'ユーザーの一連の会話。ブラウザを閉じるまでを1セッションとする。'),
            ('ターン数', '1セッション内の会話（質問→回答）の往復回数。'),
        ]

        glossary_data = [[
            Paragraph('指標', self.styles['JTableHeader']),
            Paragraph('説明', self.styles['JTableHeader'])
        ]]
        for term, desc in definitions:
            glossary_data.append([
                self._p(term, 'JTableCellSmall'),
                self._p(desc, 'JTableCellSmall')
            ])

        glossary_table = Table(glossary_data, colWidths=[40*mm, 125*mm])
        glossary_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), self.PRIMARY_COLOR),
            ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
            ('TOPPADDING', (0, 0), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))
        self.elements.append(glossary_table)

        self.elements.append(Spacer(1, 15*mm))

        # 目安
        self.elements.append(Paragraph("目標値の目安", self.styles['JHeading2']))
        target_data = [
            [Paragraph('指標', self.styles['JTableHeader']),
             Paragraph('目標', self.styles['JTableHeader']),
             Paragraph('備考', self.styles['JTableHeader'])],
            [self._p('自動解決率', 'JTableCellSmall'),
             self._p('80%以上', 'JTableCellSmall'),
             self._p('業界標準は60-70%程度', 'JTableCellSmall')],
            [self._p('満足度', 'JTableCellSmall'),
             self._p('90%以上', 'JTableCellSmall'),
             self._p('フィードバックがある回答の満足度', 'JTableCellSmall')],
            [self._p('未解決率', 'JTableCellSmall'),
             self._p('20%以下', 'JTableCellSmall'),
             self._p('低いほど良い', 'JTableCellSmall')],
        ]
        target_table = Table(target_data, colWidths=[40*mm, 40*mm, 85*mm])
        target_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), self.SUCCESS_COLOR),
            ('GRID', (0, 0), (-1, -1), 0.5, self.GRAY_COLOR),
            ('TOPPADDING', (0, 0), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ]))
        self.elements.append(target_table)

    def _format_change(self, value: float, inverse: bool = False) -> str:
        """増減値をフォーマット（文字列）"""
        if value > 0:
            sign = '+'
            color = 'green' if not inverse else 'red'
        elif value < 0:
            sign = ''
            color = 'red' if not inverse else 'green'
        else:
            return '±0%'

        return f"<font color='{color}'>{sign}{value}%</font>"

    def _format_change_p(self, value: float, inverse: bool = False) -> Paragraph:
        """増減値をフォーマット（Paragraphオブジェクト）"""
        html = self._format_change(value, inverse)
        return Paragraph(html, self.styles['JTableCellRight'])
