import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import smtplib
import imaplib
import email
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import decode_header
import threading
import os


class App40:
    """メールクライアント UI"""

    def __init__(self, root):
        self.root = root
        self.root.title("メールクライアント UI")
        self.root.geometry("1000x857")
        self.root.minsize(1000, 857)
        self.root.configure(bg="#f8f9fc")
        self._messages = []
        self._imap = None
        self._build_ui()

    def _build_ui(self):
        header = tk.Frame(self.root, bg="#1a237e", pady=8)
        header.pack(fill=tk.X)
        tk.Label(header, text="📧 メールクライアント UI",
                 font=("Noto Sans JP", 13, "bold"),
                 bg="#1a237e", fg="white").pack(side=tk.LEFT, padx=12)

        notebook = ttk.Notebook(self.root)
        notebook.pack(fill=tk.BOTH, expand=True, padx=6, pady=6)

        # ── 受信タブ ──────────────────────────────────────────────
        recv_tab = tk.Frame(notebook, bg="#f8f9fc")
        notebook.add(recv_tab, text="📥 受信")
        self._build_recv_tab(recv_tab)

        # ── 送信タブ ──────────────────────────────────────────────
        send_tab = tk.Frame(notebook, bg="#f8f9fc")
        notebook.add(send_tab, text="✉ 送信")
        self._build_send_tab(send_tab)

        # ── アカウント設定タブ ────────────────────────────────────
        settings_tab = tk.Frame(notebook, bg="#f8f9fc")
        notebook.add(settings_tab, text="⚙ 設定")
        self._build_settings_tab(settings_tab)

        self.status_var = tk.StringVar(value="設定タブでアカウントを設定してください")
        tk.Label(self.root, textvariable=self.status_var,
                 bg="#dde", font=("Arial", 9), anchor="w", padx=8
                 ).pack(fill=tk.X, side=tk.BOTTOM)

    # ── 受信タブ ──────────────────────────────────────────────────

    def _build_recv_tab(self, parent):
        # ツールバー
        bar = tk.Frame(parent, bg="#f8f9fc")
        bar.pack(fill=tk.X, padx=8, pady=6)
        ttk.Button(bar, text="🔄 メールを取得",
                   command=self._fetch_mail).pack(side=tk.LEFT, padx=4)
        tk.Label(bar, text="フォルダ:", bg="#f8f9fc",
                 font=("Arial", 9)).pack(side=tk.LEFT, padx=(12, 2))
        self.folder_var = tk.StringVar(value="INBOX")
        self.folder_cb = ttk.Combobox(bar, textvariable=self.folder_var,
                                       values=["INBOX", "Sent", "Drafts", "Trash"],
                                       width=14)
        self.folder_cb.pack(side=tk.LEFT, padx=4)
        tk.Label(bar, text="件数:", bg="#f8f9fc",
                 font=("Arial", 9)).pack(side=tk.LEFT, padx=(8, 2))
        self.fetch_count_var = tk.IntVar(value=20)
        ttk.Spinbox(bar, from_=5, to=200,
                    textvariable=self.fetch_count_var, width=5).pack(side=tk.LEFT)

        # メール一覧 + 本文
        paned = ttk.PanedWindow(parent, orient=tk.VERTICAL)
        paned.pack(fill=tk.BOTH, expand=True, padx=8)

        # メール一覧
        list_f = tk.Frame(paned, bg="#f8f9fc")
        paned.add(list_f, weight=1)
        cols = ("from_", "subject", "date", "size")
        self.mail_tree = ttk.Treeview(list_f, columns=cols,
                                       show="headings", height=8)
        for c, h, w in [("from_", "差出人", 180), ("subject", "件名", 300),
                         ("date", "日時", 140), ("size", "サイズ", 70)]:
            self.mail_tree.heading(c, text=h)
            self.mail_tree.column(c, width=w, minwidth=40)
        sb = ttk.Scrollbar(list_f, command=self.mail_tree.yview)
        self.mail_tree.configure(yscrollcommand=sb.set)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        self.mail_tree.pack(fill=tk.BOTH, expand=True)
        self.mail_tree.bind("<<TreeviewSelect>>", self._on_mail_select)
        self.mail_tree.tag_configure("unread", font=("Arial", 10, "bold"))
        self.mail_tree.tag_configure("read", font=("Arial", 10))

        # 本文
        body_f = ttk.LabelFrame(paned, text="本文", padding=4)
        paned.add(body_f, weight=2)

        # ヘッダー情報
        self.mail_header_var = tk.StringVar(value="")
        tk.Label(body_f, textvariable=self.mail_header_var,
                 bg="#e8eaf6", fg="#283593", font=("Arial", 9),
                 anchor="w", padx=6).pack(fill=tk.X, pady=2)

        self.body_text = tk.Text(body_f, bg="#ffffff", fg="#333",
                                  font=("Arial", 11), relief=tk.FLAT,
                                  state=tk.DISABLED, wrap=tk.WORD)
        body_sb = ttk.Scrollbar(body_f, command=self.body_text.yview)
        self.body_text.configure(yscrollcommand=body_sb.set)
        body_sb.pack(side=tk.RIGHT, fill=tk.Y)
        self.body_text.pack(fill=tk.BOTH, expand=True)

        # 返信ボタン
        btn_f = tk.Frame(parent, bg="#f8f9fc")
        btn_f.pack(fill=tk.X, padx=8, pady=4)
        ttk.Button(btn_f, text="↩ 返信",
                   command=self._reply).pack(side=tk.LEFT, padx=4)
        ttk.Button(btn_f, text="🗑 削除",
                   command=self._delete_mail).pack(side=tk.LEFT, padx=4)

        self.recv_progress = ttk.Progressbar(parent, mode="indeterminate")
        self.recv_progress.pack(fill=tk.X, padx=8, pady=2)

    # ── 送信タブ ──────────────────────────────────────────────────

    def _build_send_tab(self, parent):
        lbl_s = {"bg": "#f8f9fc", "font": ("Arial", 10)}

        for lbl, attr, show in [("宛先 (To):", "to_var", ""),
                                 ("CC:", "cc_var", ""),
                                 ("件名:", "subject_var", "")]:
            row = tk.Frame(parent, bg="#f8f9fc")
            row.pack(fill=tk.X, padx=8, pady=3)
            tk.Label(row, text=lbl, width=12, anchor="e", **lbl_s).pack(side=tk.LEFT)
            var = tk.StringVar()
            setattr(self, attr, var)
            ttk.Entry(row, textvariable=var, width=60).pack(side=tk.LEFT, padx=4,
                                                              fill=tk.X, expand=True)

        # 本文
        tk.Label(parent, text="本文:", **lbl_s).pack(anchor="w", padx=8, pady=2)
        self.send_body = tk.Text(parent, bg="#ffffff", fg="#333",
                                  font=("Arial", 11), relief=tk.FLAT,
                                  height=16)
        sb = ttk.Scrollbar(parent, command=self.send_body.yview)
        self.send_body.configure(yscrollcommand=sb.set)
        sb.pack(side=tk.RIGHT, fill=tk.Y)
        self.send_body.pack(fill=tk.BOTH, expand=True, padx=8, pady=4)

        btn_f = tk.Frame(parent, bg="#f8f9fc")
        btn_f.pack(fill=tk.X, padx=8, pady=4)
        ttk.Button(btn_f, text="📤 送信",
                   command=self._send_mail).pack(side=tk.LEFT, padx=4)
        ttk.Button(btn_f, text="🗑 クリア",
                   command=self._clear_compose).pack(side=tk.LEFT, padx=4)

        self.send_progress = ttk.Progressbar(parent, mode="indeterminate")
        self.send_progress.pack(fill=tk.X, padx=8, pady=2)

    # ── 設定タブ ──────────────────────────────────────────────────

    def _build_settings_tab(self, parent):
        lbl_s = {"bg": "#f8f9fc", "font": ("Arial", 10)}

        # 送信 (SMTP)
        smtp_f = ttk.LabelFrame(parent, text="SMTP設定（送信）", padding=10)
        smtp_f.pack(fill=tk.X, padx=8, pady=6)
        for lbl, attr, val, show in [
            ("SMTPサーバー:", "smtp_host_var", "smtp.gmail.com", ""),
            ("ポート:", "smtp_port_var", "587", ""),
            ("ユーザー:", "smtp_user_var", "", ""),
            ("パスワード:", "smtp_pass_var", "", "*"),
        ]:
            row = tk.Frame(smtp_f, bg=ttk.Style().lookup("TLabelframe", "background"))
            row.pack(fill=tk.X, pady=2)
            tk.Label(row, text=lbl, width=16, anchor="e",
                     bg=row.cget("bg"),
                     font=("Arial", 10)).pack(side=tk.LEFT)
            var = tk.StringVar(value=val)
            setattr(self, attr, var)
            ttk.Entry(row, textvariable=var, width=34, show=show).pack(side=tk.LEFT, padx=4)

        self.smtp_tls_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(smtp_f, text="TLS/STARTTLS を使用",
                         variable=self.smtp_tls_var).pack(anchor="w")

        # 受信 (IMAP)
        imap_f = ttk.LabelFrame(parent, text="IMAP設定（受信）", padding=10)
        imap_f.pack(fill=tk.X, padx=8, pady=6)
        for lbl, attr, val, show in [
            ("IMAPサーバー:", "imap_host_var", "imap.gmail.com", ""),
            ("ポート:", "imap_port_var", "993", ""),
        ]:
            row = tk.Frame(imap_f, bg=ttk.Style().lookup("TLabelframe", "background"))
            row.pack(fill=tk.X, pady=2)
            tk.Label(row, text=lbl, width=16, anchor="e",
                     bg=row.cget("bg"),
                     font=("Arial", 10)).pack(side=tk.LEFT)
            var = tk.StringVar(value=val)
            setattr(self, attr, var)
            ttk.Entry(row, textvariable=var, width=34, show=show).pack(side=tk.LEFT, padx=4)

        self.imap_ssl_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(imap_f, text="SSL を使用",
                         variable=self.imap_ssl_var).pack(anchor="w")

        # メモ
        tk.Label(parent,
                 text="※ Gmailの場合はアプリパスワードを使用してください。\n"
                      "  Google アカウント → セキュリティ → アプリパスワードで生成。",
                 bg="#fff3cd", fg="#856404", font=("Arial", 9),
                 anchor="w", padx=8, justify=tk.LEFT).pack(fill=tk.X, padx=8, pady=4)

        ttk.Button(parent, text="💾 設定を保存 / 接続テスト",
                   command=self._test_connection).pack(padx=8, pady=4)

    # ── メール操作 ────────────────────────────────────────────────

    def _fetch_mail(self):
        host = self.imap_host_var.get().strip()
        if not host:
            messagebox.showwarning("警告", "設定タブでIMAPサーバーを設定してください")
            return
        self.recv_progress.start(10)
        self.status_var.set("メール取得中...")
        threading.Thread(target=self._do_fetch_mail, daemon=True).start()

    def _do_fetch_mail(self):
        try:
            host = self.imap_host_var.get().strip()
            port = int(self.imap_port_var.get() or 993)
            user = self.smtp_user_var.get().strip()
            passwd = self.smtp_pass_var.get()
            folder = self.folder_var.get()
            count = self.fetch_count_var.get()

            if self.imap_ssl_var.get():
                imap = imaplib.IMAP4_SSL(host, port)
            else:
                imap = imaplib.IMAP4(host, port)

            imap.login(user, passwd)
            imap.select(folder)

            # 最新N件のIDを取得
            _, data = imap.search(None, "ALL")
            ids = data[0].split()
            fetch_ids = ids[-count:] if len(ids) >= count else ids
            fetch_ids = list(reversed(fetch_ids))

            messages = []
            for uid in fetch_ids:
                _, msg_data = imap.fetch(uid, "(RFC822.SIZE FLAGS RFC822.HEADER)")
                if not msg_data or not msg_data[0]:
                    continue
                raw_header = None
                size = 0
                flags = ""
                for part in msg_data:
                    if isinstance(part, tuple):
                        info = part[0].decode()
                        if "RFC822.SIZE" in info:
                            import re
                            m = re.search(r"RFC822\.SIZE (\d+)", info)
                            if m:
                                size = int(m.group(1))
                        if "FLAGS" in info:
                            flags = info
                        raw_header = part[1]
                if raw_header is None:
                    continue
                try:
                    msg = email.message_from_bytes(raw_header)
                    from_ = self._decode_header(msg.get("From", ""))
                    subject = self._decode_header(msg.get("Subject", ""))
                    date = msg.get("Date", "")[:25]
                    unread = "\\Seen" not in flags
                    messages.append({
                        "uid": uid,
                        "from": from_,
                        "subject": subject,
                        "date": date,
                        "size": size,
                        "unread": unread,
                    })
                except Exception:
                    pass

            self._imap = imap
            self._messages = messages
            self.root.after(0, self._show_mail_list, messages)
        except Exception as e:
            self.root.after(0, self.recv_progress.stop)
            self.root.after(0, self.status_var.set, f"取得エラー: {e}")

    def _show_mail_list(self, messages):
        self.recv_progress.stop()
        self.mail_tree.delete(*self.mail_tree.get_children())
        for msg in messages:
            tag = "unread" if msg.get("unread") else "read"
            self.mail_tree.insert("", "end",
                                   iid=str(msg["uid"]),
                                   values=(msg["from"][:30],
                                           msg["subject"][:60],
                                           msg["date"],
                                           self._fmt_size(msg["size"])),
                                   tags=(tag,))
        self.status_var.set(f"{len(messages)} 件取得")

    def _on_mail_select(self, event):
        sel = self.mail_tree.selection()
        if not sel or not self._imap:
            return
        uid = sel[0].encode()
        threading.Thread(target=self._load_mail_body,
                         args=(uid,), daemon=True).start()

    def _load_mail_body(self, uid):
        try:
            _, data = self._imap.fetch(uid, "(RFC822)")
            if not data or not data[0]:
                return
            raw = data[0][1]
            msg = email.message_from_bytes(raw)
            from_ = self._decode_header(msg.get("From", ""))
            subject = self._decode_header(msg.get("Subject", ""))
            date = msg.get("Date", "")
            body = self._extract_body(msg)
            self.root.after(0, self._show_body, from_, subject, date, body)
        except Exception as e:
            self.root.after(0, self.status_var.set, f"本文取得エラー: {e}")

    def _extract_body(self, msg):
        body = ""
        if msg.is_multipart():
            for part in msg.walk():
                ct = part.get_content_type()
                cd = str(part.get("Content-Disposition") or "")
                if ct == "text/plain" and "attachment" not in cd:
                    payload = part.get_payload(decode=True)
                    if payload:
                        charset = part.get_content_charset() or "utf-8"
                        body = payload.decode(charset, errors="replace")
                        break
        else:
            payload = msg.get_payload(decode=True)
            if payload:
                charset = msg.get_content_charset() or "utf-8"
                body = payload.decode(charset, errors="replace")
        return body

    def _show_body(self, from_, subject, date, body):
        self.mail_header_var.set(f"From: {from_}  |  件名: {subject}  |  日付: {date}")
        self.body_text.config(state=tk.NORMAL)
        self.body_text.delete("1.0", tk.END)
        self.body_text.insert("1.0", body)
        self.body_text.config(state=tk.DISABLED)

    def _decode_header(self, value):
        if not value:
            return ""
        parts = decode_header(value)
        decoded = []
        for part, enc in parts:
            if isinstance(part, bytes):
                decoded.append(part.decode(enc or "utf-8", errors="replace"))
            else:
                decoded.append(part)
        return "".join(decoded)

    def _reply(self):
        sel = self.mail_tree.selection()
        if not sel:
            return
        uid = sel[0]
        msg_data = next((m for m in self._messages if str(m["uid"]) == uid), None)
        if msg_data:
            self.to_var.set(msg_data.get("from", ""))
            self.subject_var.set("Re: " + msg_data.get("subject", ""))

    def _delete_mail(self):
        sel = self.mail_tree.selection()
        if not sel:
            return
        if messagebox.askyesno("確認", "選択したメールを削除しますか?") and self._imap:
            for uid in sel:
                try:
                    self._imap.store(uid.encode(), "+FLAGS", "\\Deleted")
                except Exception:
                    pass
            try:
                self._imap.expunge()
            except Exception:
                pass
            self._fetch_mail()

    # ── 送信 ─────────────────────────────────────────────────────

    def _send_mail(self):
        to = self.to_var.get().strip()
        if not to:
            messagebox.showwarning("警告", "宛先を入力してください")
            return
        host = self.smtp_host_var.get().strip()
        if not host:
            messagebox.showwarning("警告", "設定タブでSMTPサーバーを設定してください")
            return
        self.send_progress.start(10)
        threading.Thread(target=self._do_send_mail, daemon=True).start()

    def _do_send_mail(self):
        try:
            host = self.smtp_host_var.get().strip()
            port = int(self.smtp_port_var.get() or 587)
            user = self.smtp_user_var.get().strip()
            passwd = self.smtp_pass_var.get()
            to = self.to_var.get().strip()
            cc = self.cc_var.get().strip()
            subject = self.subject_var.get().strip()
            body = self.send_body.get("1.0", tk.END).strip()

            msg = MIMEMultipart("alternative")
            msg["From"] = user
            msg["To"] = to
            if cc:
                msg["Cc"] = cc
            msg["Subject"] = subject
            msg.attach(MIMEText(body, "plain", "utf-8"))

            if self.smtp_tls_var.get():
                smtp = smtplib.SMTP(host, port, timeout=15)
                smtp.ehlo()
                smtp.starttls()
                smtp.ehlo()
            else:
                smtp = smtplib.SMTP_SSL(host, port, timeout=15)

            smtp.login(user, passwd)
            recipients = [to] + ([cc] if cc else [])
            smtp.sendmail(user, recipients, msg.as_string())
            smtp.quit()

            self.root.after(0, self.send_progress.stop)
            self.root.after(0, self.status_var.set, f"送信完了: {to}")
            self.root.after(0, messagebox.showinfo, "完了", "メールを送信しました")
        except Exception as e:
            self.root.after(0, self.send_progress.stop)
            self.root.after(0, messagebox.showerror, "送信エラー", str(e))

    def _clear_compose(self):
        self.to_var.set("")
        self.cc_var.set("")
        self.subject_var.set("")
        self.send_body.delete("1.0", tk.END)

    def _test_connection(self):
        host = self.smtp_host_var.get().strip()
        if not host:
            messagebox.showwarning("警告", "SMTPサーバーを入力してください")
            return
        self.status_var.set("接続テスト中...")
        threading.Thread(target=self._do_test_connection, daemon=True).start()

    def _do_test_connection(self):
        try:
            host = self.smtp_host_var.get().strip()
            port = int(self.smtp_port_var.get() or 587)
            if self.smtp_tls_var.get():
                smtp = smtplib.SMTP(host, port, timeout=10)
                smtp.ehlo()
                smtp.starttls()
            else:
                smtp = smtplib.SMTP_SSL(host, port, timeout=10)
            smtp.quit()
            self.root.after(0, self.status_var.set,
                            f"✅ SMTP接続成功: {host}:{port}")
        except Exception as e:
            self.root.after(0, self.status_var.set,
                            f"❌ SMTP接続失敗: {e}")

    def _fmt_size(self, size):
        if size < 1024:
            return f"{size}B"
        elif size < 1024 * 1024:
            return f"{size//1024}KB"
        return f"{size//1024//1024}MB"


if __name__ == "__main__":
    root = tk.Tk()
    app = App40(root)
    root.mainloop()
