import datetime
import calendar
import json
import os
import shutil
import sys

# PySide6から必要なパーツをすべてインポートします
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QComboBox, QLabel, QGridLayout, QGroupBox,
    QLineEdit, QListWidget, QDialog, QRadioButton, QButtonGroup,
    QTextEdit, QMessageBox, QSizePolicy
)
from PySide6.QtCore import Qt

# データ保存用ファイル名
DATA_FILE = "calendar_data.json"

class CalendarApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("九星・六曜カレンダー")

        self.resize(550, 430)

        # 現在の日付を取得
        today = datetime.date.today()
        self.current_year = today.year
        self.current_month = today.month

        # データ管理用の辞書と選択された日付
        self.schedule_db = self.load_data()
        self.selected_date_str = today.strftime("%Y-%m-%d")

        # 日付ボタンを保持するリスト
        self.day_buttons = []

        # 画面パーツ（UI）の組み立て
        self.create_widgets()
        self.update_calendar_display()

    def load_data(self):
        """JSONファイルからデータを読み込む"""
        if os.path.exists(DATA_FILE):
            try:
                with open(DATA_FILE, "r", encoding="utf-8") as f:
                    data = json.load(f)

                    # 旧データ（文字列だけ）を新データ（辞書型）に自動変換する仕組み
                    updated_data = {}
                    for k, v in data.items():
                        if isinstance(v, str):
                            # 昔のデータは、曜日に応じたデフォルト色を割り振る
                            y, m, d = map(int, k.split("-"))
                            dt = datetime.date(y, m, d)
                            w = (dt.weekday() + 1) % 7 # 日曜日始まりに合わせる
                            if w == 0: default_color = "red"
                            elif w == 6: default_color = "blue"
                            else: default_color = "black"

                            updated_data[k] = {"text": v, "color": default_color}
                        else:
                            updated_data[k] = v
                    return updated_data
            except Exception:
                return {}
        return {}

    def save_data(self):
        """JSONファイルへデータを保存し、自動でバックアップも作成する"""
        try:
            with open(DATA_FILE, "w", encoding="utf-8") as f:
                json.dump(self.schedule_db, f, ensure_ascii=False, indent=4)

            today_str = datetime.date.today().strftime("%Y%m%d")
            backup_file = f"calendar_data_backup_{today_str}.json"

            if os.path.exists(DATA_FILE):
                shutil.copy(DATA_FILE, backup_file)

        except Exception as e:
            QMessageBox.critical(self, "エラー", f"データの保存またはバックアップに失敗しました:\n{e}")

    def calc_rokuyo_and_kyusei(self, year, month, day):
        """標準ロジックで六曜と日九星を計算する"""
        target_date = datetime.date(year, month, day)

        # --- 1. 六曜の簡易計算（旧暦近似アルゴリズム） ---
        base_date = datetime.date(2020, 1, 1)
        diff_days = (target_date - base_date).days

        approx_lunar_days = diff_days + 7
        lunar_month = int((approx_lunar_days // 29.530589) + 12) % 12
        if lunar_month == 0: lunar_month = 12
        lunar_day = int(approx_lunar_days % 29.530589) + 1
        if lunar_day > 30: lunar_day = 30

        rokuyo_list = ["大安", "赤口", "先勝", "友引", "先負", "仏滅"]
        rokuyo_index = (lunar_month + lunar_day) % 6
        rokuyo = rokuyo_list[rokuyo_index]

        # --- 2. 日九星の計算（基準日からの数理計算） ---
        kyusei_list = ["一白", "二黒", "三碧", "四緑", "五黄", "六白", "七赤", "八白", "九紫"]
        kyusei_index = (8 - (diff_days % 9)) % 9
        kyusei = kyusei_list[kyusei_index]

        return rokuyo, kyusei

    def create_widgets(self):
        """画面の部品を配置する"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(10, 10, 10, 10)
        main_layout.setSpacing(10) # 各エリアの間の隙間を一定にします

        # --- 上部ヘッダー（年月指定・移動） ---
        top_layout = QHBoxLayout()

        prev_btn = QPushButton("◀ 前月")
        prev_btn.clicked.connect(self.prev_month)
        top_layout.addWidget(prev_btn)

        select_layout = QHBoxLayout()
        select_layout.addStretch()

        self.year_combo = QComboBox()
        years = [str(y) for y in range(1920, 2081)]
        self.year_combo.addItems(years)
        self.year_combo.setCurrentText(str(self.current_year))
        self.year_combo.setFixedWidth(70)
        self.year_combo.currentTextChanged.connect(self.on_year_month_changed)
        select_layout.addWidget(self.year_combo)

        year_lbl = QLabel("年")
        select_layout.addWidget(year_lbl)

        self.month_combo = QComboBox()
        months = [str(m) for m in range(1, 13)]
        self.month_combo.addItems(months)
        self.month_combo.setCurrentText(str(self.current_month))
        self.month_combo.setFixedWidth(50)
        self.month_combo.currentTextChanged.connect(self.on_year_month_changed)
        select_layout.addWidget(self.month_combo)

        month_lbl = QLabel("月")
        select_layout.addWidget(month_lbl)
        select_layout.addStretch()

        top_layout.addLayout(select_layout)

        next_btn = QPushButton("次月 ▶")
        next_btn.clicked.connect(self.next_month)
        top_layout.addWidget(next_btn)

        main_layout.addLayout(top_layout)

        # --- 中央カレンダー（グリッド表示用） ---
        self.cal_container = QWidget()
        cal_layout = QVBoxLayout(self.cal_container)
        cal_layout.setContentsMargins(0, 5, 0, 5)

        week_layout = QHBoxLayout()
        weekdays = ["日", "月", "火", "水", "木", "金", "土"]
        colors = ["red", "black", "black", "black", "black", "black", "blue"]

        for day_name, color in zip(weekdays, colors):
            lbl = QLabel(day_name)
            lbl.setAlignment(Qt.AlignCenter)
            lbl.setStyleSheet(f"font-weight: bold; font-size: 14px; color: {color};")
            week_layout.addWidget(lbl)

        cal_layout.addLayout(week_layout)

        self.grid_widget = QWidget()
        self.grid_layout = QGridLayout(self.grid_widget)
        self.grid_layout.setSpacing(4)
        self.grid_layout.setContentsMargins(0, 0, 0, 0)

        cal_layout.addWidget(self.grid_widget)

        # 【変更】カレンダーエリアも自動で伸びないようにガッチリ固定します
        self.cal_container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        main_layout.addWidget(self.cal_container)

        # --- 下部検索エリア ---
        search_group = QGroupBox("日記・予定の検索")
        search_layout = QVBoxLayout(search_group)
        search_layout.setContentsMargins(10, 8, 10, 8)
        search_layout.setSpacing(6)

        search_input_layout = QHBoxLayout()
        self.search_entry = QLineEdit()
        self.search_entry.returnPressed.connect(self.search_events)
        search_input_layout.addWidget(self.search_entry)

        search_btn = QPushButton("検索")
        search_btn.clicked.connect(self.search_events)
        search_input_layout.addWidget(search_btn)
        search_layout.addLayout(search_input_layout)

        self.result_listbox = QListWidget()
        self.result_listbox.setStyleSheet("font-family: 'Courier'; font-size: 11px;")
        self.result_listbox.setFixedHeight(125)
        self.result_listbox.itemClicked.connect(self.on_search_result_click)
        search_layout.addWidget(self.result_listbox)

        search_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        main_layout.addWidget(search_group)

        # 【新機能】一番下に「見えないクッション」を挟んで、上のパーツをすべて上に押し詰めます
        main_layout.addStretch()

    def update_calendar_display(self):
        """カレンダー表示を最新に更新する（グリッド再描画）"""
        self.year_combo.blockSignals(True)
        self.month_combo.blockSignals(True)
        self.year_combo.setCurrentText(str(self.current_year))
        self.month_combo.setCurrentText(str(self.current_month))
        self.year_combo.blockSignals(False)
        self.month_combo.blockSignals(False)

        for btn in self.day_buttons:
            self.grid_layout.removeWidget(btn)
            btn.deleteLater()
        self.day_buttons.clear()

        first_weekday, num_days = calendar.monthrange(self.current_year, self.current_month)
        start_col = (first_weekday + 1) % 7

        row = 0
        col = start_col

        for day in range(1, num_days + 1):
            current_date = datetime.date(self.current_year, self.current_month, day)
            date_str = current_date.strftime("%Y-%m-%d")

            event_data = self.schedule_db.get(date_str, {})
            event_text = event_data.get("text", "")

            if col == 0:
                default_fg = "red"
            elif col == 6:
                default_fg = "blue"
            else:
                default_fg = "black"

            if event_text:
                marker_color = event_data.get("color", default_fg)
                event_marker = "\n⚫︎"
                fg_color = marker_color
            else:
                event_marker = "\n"
                fg_color = default_fg

            btn_text = f"{day}{event_marker}"

            btn = QPushButton(btn_text)
            btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            padding_style = "padding: 4px 0px;"

            if date_str == self.selected_date_str:
                btn.setStyleSheet(f"""
                    QPushButton {{
                        background-color: #FFF9C4;
                        color: {fg_color};
                        font-weight: bold;
                        font-size: 13px;
                        border: 2px solid #FFA000;
                        border-radius: 4px;
                        {padding_style}
                    }}
                """)
            else:
                btn.setStyleSheet(f"""
                    QPushButton {{
                        background-color: #F5F5F5;
                        color: {fg_color};
                        font-weight: bold;
                        font-size: 13px;
                        border: 1px solid #D3D3D3;
                        border-radius: 4px;
                        {padding_style}
                    }}
                    QPushButton:hover {{
                        background-color: #E0E0E0;
                    }}
                """)

            btn.clicked.connect(lambda checked=False, d=day: self.on_date_click(d))

            self.grid_layout.addWidget(btn, row, col)
            self.day_buttons.append(btn)

            if event_text:
                btn.setToolTip(event_text)

            col += 1
            if col > 6:
                col = 0
                row += 1

    def on_date_click(self, day):
        """カレンダーの日付がクリックされた時の処理"""
        self.selected_date_str = f"{self.current_year}-{self.current_month:02d}-{day:02d}"
        self.update_calendar_display()
        self.open_detail_window(day)

    def open_detail_window(self, day):
        """別ウィンドウで詳細情報・入力画面を表示する"""
        current_date = datetime.date(self.current_year, self.current_month, day)
        weekday_str = ["月", "火", "水", "木", "金", "土", "日"][current_date.weekday()]
        rokuyo, kyusei = self.calc_rokuyo_and_kyusei(self.current_year, self.current_month, day)

        self.sub_win = QDialog(self)
        self.sub_win.setWindowTitle(f"{self.current_year}年{self.current_month}月{day}日の詳細")
        # 【ここを修正！】詳細画面の高さも少しコンパクトに変更しました
        self.sub_win.resize(450, 380)
        self.sub_win.setWindowModality(Qt.ApplicationModal)

        sub_layout = QVBoxLayout(self.sub_win)
        sub_layout.setContentsMargins(20, 20, 20, 20)
        sub_layout.setSpacing(10)

        lbl_date = QLabel(f"{self.current_year}年 {self.current_month}月 {day}日 ({weekday_str})")
        lbl_date.setStyleSheet("font-size: 16px; font-weight: bold;")
        sub_layout.addWidget(lbl_date)

        lbl_info = QLabel(f"六曜: 【 {rokuyo} 】   /   日家九星: 【 {kyusei} 】")
        lbl_info.setStyleSheet("font-size: 13px;")
        sub_layout.addWidget(lbl_info)

        lbl_color_title = QLabel("カレンダーに表示する丸印の色:")
        lbl_color_title.setStyleSheet("font-size: 12px; font-weight: bold; margin-top: 5px;")
        sub_layout.addWidget(lbl_color_title)

        color_widget = QWidget()
        color_layout = QHBoxLayout(color_widget)
        color_layout.setContentsMargins(0, 0, 0, 0)

        event_data = self.schedule_db.get(self.selected_date_str, {})
        current_color = event_data.get("color", "")
        if not current_color:
            w = (current_date.weekday() + 1) % 7
            if w == 0: current_color = "red"
            elif w == 6: current_color = "blue"
            else: current_color = "black"

        rb_colors = [("平日", "black"), ("土曜", "blue"), ("日祝", "red"), ("緑", "#53fa32"), ("ピンク", "#fa328c")]
        self.radio_buttons = {}
        self.color_group = QButtonGroup(self.sub_win)

        for text, color_code in rb_colors:
            rb = QRadioButton(text)
            display_color = "black" if color_code == "black" else color_code
            rb.setStyleSheet(f"color: {display_color}; font-size: 11px;")

            if color_code == current_color:
                rb.setChecked(True)

            color_layout.addWidget(rb)
            self.color_group.addButton(rb)
            self.radio_buttons[rb] = color_code

        sub_layout.addWidget(color_widget)

        lbl_text_title = QLabel("予定の内容（複数行入力可能）:")
        lbl_text_title.setStyleSheet("font-size: 12px; font-weight: bold; margin-top: 5px;")
        sub_layout.addWidget(lbl_text_title)

        self.sub_entry_event = QTextEdit()
        self.sub_entry_event.setFontPointSize(11)

        current_event = event_data.get("text", "")
        self.sub_entry_event.setPlainText(current_event)
        sub_layout.addWidget(self.sub_entry_event)

        btn_widget = QWidget()
        btn_layout = QHBoxLayout(btn_widget)
        btn_layout.setContentsMargins(0, 10, 0, 0)

        save_btn = QPushButton("予定を保存")
        save_btn.clicked.connect(self.save_sub_entry)
        btn_layout.addWidget(save_btn)

        delete_btn = QPushButton("予定を削除")
        delete_btn.clicked.connect(self.delete_sub_entry)
        btn_layout.addWidget(delete_btn)

        btn_layout.addStretch()

        close_btn = QPushButton("閉じる")
        close_btn.clicked.connect(self.sub_win.close)
        btn_layout.addWidget(close_btn)

        sub_layout.addWidget(btn_widget)
        self.sub_win.show()

    def save_sub_entry(self):
        """詳細ウィンドウの「予定を保存」ボタンが押された時の処理"""
        event_text = self.sub_entry_event.toPlainText().strip()

        checked_rb = self.color_group.checkedButton()
        selected_color = self.radio_buttons.get(checked_rb, "black")

        if event_text:
            self.schedule_db[self.selected_date_str] = {
                "text": event_text,
                "color": selected_color
            }
        else:
            self.schedule_db.pop(self.selected_date_str, None)

        self.save_data()
        self.update_calendar_display()
        self.sub_win.close()
        QMessageBox.information(self, "完了", "予定を保存しました。")

    def delete_sub_entry(self):
        """詳細ウィンドウの「予定を削除」ボタンが押された時の処理"""
        if self.selected_date_str in self.schedule_db:
            del self.schedule_db[self.selected_date_str]
            self.save_data()
            self.update_calendar_display()
            self.sub_win.close()
            QMessageBox.information(self, "完了", "予定を削除しました。")
        else:
            self.sub_win.close()

    def on_year_month_changed(self, text):
        """コンボボックスで年月が指定された時の処理"""
        self.current_year = int(self.year_combo.currentText())
        self.current_month = int(self.month_combo.currentText())
        self.reset_selection_to_first_day()

    def prev_month(self):
        """前月ボタン"""
        if self.current_month == 1:
            self.current_month = 12
            self.current_year -= 1
        else:
            self.current_month -= 1
        self.reset_selection_to_first_day()

    def next_month(self):
        """次月ボタン"""
        if self.current_month == 12:
            self.current_month = 1
            self.current_year += 1
        else:
            self.current_month += 1
        self.reset_selection_to_first_day()

    def reset_selection_to_first_day(self):
        """月移動・指定時に選択日をその月の1日に初期化する"""
        self.selected_date_str = f"{self.current_year}-{self.current_month:02d}-01"
        self.update_calendar_display()

    def search_events(self):
        """キーワードに一致する日記・予定を検索してリスト表示する"""
        query = self.search_entry.text().strip()
        self.result_listbox.clear()

        if not query:
            QMessageBox.warning(self, "注意", "検索キーワードを入力してください。")
            return

        sorted_dates = sorted(self.schedule_db.keys())
        hit_count = 0

        for date_str in sorted_dates:
            event_data = self.schedule_db[date_str]
            text = event_data.get("text", "")

            if query.lower() in text.lower():
                hit_count += 1
                clean_text = text.replace("\n", " ")
                short_text = clean_text[:30] + "..." if len(clean_text) > 30 else clean_text

                display_line = f"{date_str} | {short_text}"
                self.result_listbox.addItem(display_line)

        if hit_count == 0:
            self.result_listbox.addItem("一致する日記は見つかりませんでした。")

    def on_search_result_click(self, item):
        """検索結果をクリックした時、その日付のカレンダーにジャンプして詳細を開く"""
        selected_line = item.text()

        if "|" in selected_line:
            date_part = selected_line.split("|")[0].strip()

            try:
                y, m, d = map(int, date_part.split("-"))
                self.current_year = y
                self.current_month = m
                self.selected_date_str = date_part

                self.update_calendar_display()
                self.open_detail_window(d)

            except Exception:
                pass

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CalendarApp()
    window.show()
    sys.exit(app.exec())
