import os
import subprocess
import threading
import tkinter as tk
from tkinter import messagebox

from storage import register_user, check_user, get_repositories, add_repository, delete_repository
from git_analyzer import analyze_repository, format_analysis


class UniversalGitAnalyzerApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Universal Git Analyzer")
        self.root.geometry("1920x1080+0+0")
        self.root.minsize(1200, 720)
        self.root.resizable(True, True)
        try:
            self.root.attributes("-zoomed", True)
        except Exception:
            pass

        self.current_user = None
        self.current_repo = None
        self.current_index = None
        self.current_data = None

        self.bg = "#eef4ff"
        self.panel = "#ffffff"
        self.button = "#6aa6ff"
        self.logo_image = None
        self.loading_canvas = None
        self.loading_cat = None
        self.loading_step = 0
        self.root.configure(bg=self.bg)

        self.show_splash()
        self.root.mainloop()

    def clear(self):
        for widget in self.root.winfo_children():
            widget.destroy()

    def resource_path(self, relative_path):
        base_path = getattr(__import__("sys"), "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
        return os.path.join(base_path, relative_path)

    def play_sound(self, filename):
        path = self.resource_path(os.path.join("assets", filename))
        if not os.path.exists(path):
            return

        # Windows: проигрываем wav без внешних программ
        if os.name == "nt":
            try:
                import winsound
                winsound.PlaySound(path, winsound.SND_FILENAME | winsound.SND_ASYNC)
                return
            except Exception:
                return

        # Linux: пробуем стандартные проигрыватели звука
        for player in ("paplay", "aplay"):
            try:
                subprocess.Popen([player, path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                return
            except FileNotFoundError:
                continue
            except Exception:
                break

    def play_startup_sound(self):
        self.play_sound("startup.wav")

    def make_button(self, parent, text, command, width=22):
        return tk.Button(
            parent,
            text=text,
            command=command,
            width=width,
            bg=self.button,
            fg="white",
            activebackground="#4d8ee8",
            font=("Arial", 10, "bold"),
            relief="flat",
            cursor="hand2",
            pady=6,
            wraplength=360,
        )

    def show_splash(self):
        self.clear()
        self.play_startup_sound()

        frame = tk.Frame(self.root, bg="#07101f")
        frame.pack(fill="both", expand=True)

        logo_path = self.resource_path(os.path.join("assets", "logo.png"))
        if os.path.exists(logo_path):
            try:
                self.logo_image = tk.PhotoImage(file=logo_path)
                if self.logo_image.width() > 560:
                    factor = max(1, (self.logo_image.width() + 519) // 520)
                    self.logo_image = self.logo_image.subsample(factor, factor)
                tk.Label(frame, image=self.logo_image, bg="#07101f").pack(pady=(25, 8))
            except Exception:
                tk.Label(frame, text="Universal Git Analyzer", font=("Arial", 30, "bold"), bg="#07101f", fg="white").pack(pady=80)
        else:
            tk.Label(frame, text="Universal Git Analyzer", font=("Arial", 30, "bold"), bg="#07101f", fg="white").pack(pady=80)

        tk.Label(frame, text="Загрузка проекта...", font=("Arial", 13, "bold"), bg="#07101f", fg="#dce8ff").pack(pady=5)

        self.loading_canvas = tk.Canvas(frame, width=640, height=100, bg="#07101f", highlightthickness=0)
        self.loading_canvas.pack(pady=(45, 0))
        self.draw_loading_bar()
        self.animate_loading_cat()

        self.root.after(4500, self.show_login)


    def draw_loading_bar(self):
        if not self.loading_canvas:
            return
        c = self.loading_canvas
        c.delete("all")
        c.create_line(70, 58, 545, 58, fill="#2a3e63", width=6, capstyle="round")
        c.create_line(70, 58, 545, 58, fill="#4e8cff", width=2, capstyle="round")
        # Чашка Gitea справа
        c.create_oval(545, 30, 595, 80, fill="#24551f", outline="#7dd36f", width=2)
        c.create_arc(585, 42, 620, 70, start=-70, extent=140, outline="#7dd36f", width=4, style="arc")
        c.create_rectangle(559, 45, 582, 69, fill="#24551f", outline="#24551f")
        c.create_text(570, 57, text="☕", fill="#e8ffe4", font=("Arial", 18, "bold"))
        c.create_text(570, 88, text="Gitea", fill="#9dc8ff", font=("Arial", 9, "bold"))

    def animate_loading_cat(self):
        if not self.loading_canvas:
            return
        c = self.loading_canvas
        c.delete("cat")
        steps = 90
        x = 70 + int((455 * self.loading_step) / steps)
        y = 58
        # Чёрный кот, который бежит к чаю
        c.create_oval(x - 18, y - 22, x + 18, y + 14, fill="#02050a", outline="white", width=2, tags="cat")
        c.create_polygon(x - 16, y - 16, x - 7, y - 34, x + 0, y - 16, fill="#02050a", outline="white", width=2, tags="cat")
        c.create_polygon(x + 4, y - 16, x + 13, y - 34, x + 20, y - 16, fill="#02050a", outline="white", width=2, tags="cat")
        c.create_line(x - 18, y - 1, x - 36, y - 8, fill="white", width=2, tags="cat")
        c.create_line(x - 18, y + 5, x - 38, y + 5, fill="white", width=2, tags="cat")
        c.create_line(x + 18, y - 1, x + 36, y - 8, fill="white", width=2, tags="cat")
        c.create_line(x + 18, y + 5, x + 38, y + 5, fill="white", width=2, tags="cat")
        c.create_arc(x - 36, y - 2, x - 5, y + 35, start=190, extent=170, outline="white", width=3, style="arc", tags="cat")
        c.create_text(x, y + 31, text="🐾", fill="#dce8ff", font=("Arial", 12), tags="cat")

        self.loading_step += 1
        if self.loading_step <= steps:
            self.root.after(45, self.animate_loading_cat)

    def show_login(self):
        self.clear()
        frame = tk.Frame(self.root, bg=self.panel, padx=35, pady=35)
        frame.place(relx=0.5, rely=0.5, anchor="center")
        tk.Label(frame, text="Вход в аккаунт", font=("Arial", 22, "bold"), bg=self.panel).pack(pady=10)
        tk.Label(frame, text="Логин", bg=self.panel, font=("Arial", 11)).pack(anchor="w")
        login_entry = tk.Entry(frame, width=35, font=("Arial", 12))
        login_entry.pack(pady=5)
        tk.Label(frame, text="Пароль", bg=self.panel, font=("Arial", 11)).pack(anchor="w")
        password_entry = tk.Entry(frame, width=35, font=("Arial", 12), show="*")
        password_entry.pack(pady=5)

        def login():
            login_value = login_entry.get().strip()
            password_value = password_entry.get().strip()
            if not login_value or not password_value:
                messagebox.showerror("Ошибка", "Введите логин и пароль")
                return
            if check_user(login_value, password_value):
                self.current_user = login_value
                self.show_main_screen()
            else:
                messagebox.showerror("Ошибка", "Неверный логин или пароль")

        self.make_button(frame, "Войти", login).pack(pady=12)
        self.make_button(frame, "Создать профиль", self.show_register).pack(pady=5)

    def show_register(self):
        self.clear()
        frame = tk.Frame(self.root, bg=self.panel, padx=35, pady=35)
        frame.place(relx=0.5, rely=0.5, anchor="center")
        tk.Label(frame, text="Создание профиля", font=("Arial", 22, "bold"), bg=self.panel).pack(pady=10)
        tk.Label(frame, text="Логин", bg=self.panel, font=("Arial", 11)).pack(anchor="w")
        login_entry = tk.Entry(frame, width=35, font=("Arial", 12))
        login_entry.pack(pady=5)
        tk.Label(frame, text="Пароль", bg=self.panel, font=("Arial", 11)).pack(anchor="w")
        password_entry = tk.Entry(frame, width=35, font=("Arial", 12), show="*")
        password_entry.pack(pady=5)

        def register():
            login_value = login_entry.get().strip()
            password_value = password_entry.get().strip()
            if not login_value or not password_value:
                messagebox.showerror("Ошибка", "Введите логин и пароль")
                return
            if len(password_value) < 4:
                messagebox.showerror("Ошибка", "Пароль должен быть минимум 4 символа")
                return
            ok, text = register_user(login_value, password_value)
            if ok:
                messagebox.showinfo("Готово", text)
                self.show_login()
            else:
                messagebox.showerror("Ошибка", text)

        self.make_button(frame, "Зарегистрироваться", register).pack(pady=12)
        self.make_button(frame, "Назад", self.show_login).pack(pady=5)

    def show_main_screen(self):
        self.clear()
        top = tk.Frame(self.root, bg="#1d3557", height=55)
        top.pack(fill="x")
        tk.Label(top, text=f"Universal Git Analyzer | Пользователь: {self.current_user}", bg="#1d3557", fg="white", font=("Arial", 16, "bold")).pack(side="left", padx=15, pady=12)
        tk.Button(top, text="Выйти", command=self.show_login, bg="#e63946", fg="white", relief="flat", font=("Arial", 11)).pack(side="right", padx=15)

        content = tk.Frame(self.root, bg=self.bg)
        content.pack(fill="both", expand=True, padx=10, pady=10)
        left = tk.Frame(content, bg=self.panel, padx=12, pady=12, width=520)
        left.pack(side="left", fill="y")
        left.pack_propagate(False)
        right = tk.Frame(content, bg=self.panel, padx=10, pady=10)
        right.pack(side="right", fill="both", expand=True, padx=(10, 0))

        tk.Label(left, text="Мои репозитории", bg=self.panel, font=("Arial", 20, "bold")).pack(anchor="w")
        self.repo_listbox = tk.Listbox(left, width=58, height=20, font=("Arial", 11))
        self.repo_listbox.pack(fill="x", pady=8)
        self.repo_listbox.bind("<<ListboxSelect>>", lambda event: self.on_repo_click())

        tk.Label(left, text="Новая ссылка", bg=self.panel, font=("Arial", 11)).pack(anchor="w")
        self.repo_entry = tk.Entry(left, font=("Arial", 11))
        self.repo_entry.pack(fill="x", pady=5)

        self.make_button(left, "Сохранить ссылку", self.save_repo, width=42).pack(fill="x", pady=4)
        self.make_button(left, "Обновить выбранный репозиторий", self.refresh_selected, width=42).pack(fill="x", pady=4)
        self.make_button(left, "Обновить все репозитории", self.refresh_all, width=42).pack(fill="x", pady=4)
        self.make_button(left, "Удалить ссылку", self.remove_repo, width=42).pack(fill="x", pady=4)

        text_frame = tk.Frame(right, bg=self.panel)
        text_frame.pack(fill="both", expand=True)
        self.result_text = tk.Text(text_frame, font=("Consolas", 11), wrap="word")
        self.result_text.pack(side="left", fill="both", expand=True)
        scrollbar = tk.Scrollbar(text_frame, command=self.result_text.yview)
        scrollbar.pack(side="right", fill="y")
        self.result_text.config(yscrollcommand=scrollbar.set)
        self.result_text.config(state="disabled")

        nav = tk.Frame(right, bg=self.panel)
        nav.pack(fill="x", pady=(6, 0))
        self.prev_button = self.make_button(nav, "<-", self.prev_commit, width=8)
        self.prev_button.pack(side="left")
        self.version_label = tk.Label(nav, text="Версия: —", bg=self.panel, font=("Arial", 11, "bold"))
        self.version_label.pack(side="left", padx=12)
        self.next_button = self.make_button(nav, "->", self.next_commit, width=8)
        self.next_button.pack(side="left")

        self.refresh_list()

    def set_text(self, text):
        self.result_text.config(state="normal")
        self.result_text.delete("1.0", tk.END)
        self.result_text.insert(tk.END, text)
        self.result_text.config(state="disabled")

    def refresh_list(self):
        self.repo_listbox.delete(0, tk.END)
        for repo in get_repositories(self.current_user):
            self.repo_listbox.insert(tk.END, repo)

    def get_selected_repo(self):
        selection = self.repo_listbox.curselection()
        if not selection:
            return None
        return self.repo_listbox.get(selection[0])

    def save_repo(self):
        link = self.repo_entry.get().strip()
        if not link:
            messagebox.showerror("Ошибка", "Введите ссылку")
            return
        added = add_repository(self.current_user, link)
        if added:
            self.repo_entry.delete(0, tk.END)
            self.refresh_list()
            messagebox.showinfo("Готово", "Ссылка сохранена")
        else:
            messagebox.showinfo("Информация", "Эта ссылка уже сохранена")

    def remove_repo(self):
        link = self.get_selected_repo()
        if not link:
            messagebox.showerror("Ошибка", "Выберите репозиторий")
            return
        delete_repository(self.current_user, link)
        self.refresh_list()
        self.current_repo = None
        self.current_index = None
        self.current_data = None
        self.set_text("")
        self.version_label.config(text="Версия: —")

    def on_repo_click(self):
        link = self.get_selected_repo()
        if link:
            self.load_repo(link, selected_index=None)

    def refresh_selected(self):
        link = self.get_selected_repo() or self.current_repo
        if not link:
            messagebox.showerror("Ошибка", "Выберите репозиторий")
            return
        self.load_repo(link, self.current_index)

    def refresh_all(self):
        repos = get_repositories(self.current_user)
        if not repos:
            messagebox.showinfo("Информация", "Сохранённых репозиториев нет")
            return
        self.set_text("Обновление всех репозиториев...\n")
        def worker():
            results = []
            for repo in repos:
                try:
                    data = analyze_repository(repo)
                    results.append(format_analysis(data))
                except Exception as e:
                    results.append(f"Ссылка: {repo}\nОшибка: {e}")
            self.root.after(0, lambda: self.set_text("\n\n" + "=" * 70 + "\n\n".join(results)))
        threading.Thread(target=worker, daemon=True).start()

    def load_repo(self, link, selected_index=None):
        self.current_repo = link
        self.set_text("Загрузка данных...\n")
        self.version_label.config(text="Версия: загрузка...")

        def worker():
            try:
                data = analyze_repository(link, selected_index)
                self.root.after(0, lambda: self.show_analysis(data))
            except Exception as e:
                self.root.after(0, lambda: self.show_error(e))
        threading.Thread(target=worker, daemon=True).start()

    def show_analysis(self, data):
        self.current_data = data
        self.current_index = data["selected_index"]
        self.set_text(format_analysis(data))
        commit = data["selected_commit"]
        self.version_label.config(text=f"Версия: {self.current_index + 1} / {len(data['commits'])} ({commit['short']})")

    def show_error(self, error):
        self.set_text("")
        self.version_label.config(text="Версия: —")
        messagebox.showerror("Ошибка", str(error))

    def prev_commit(self):
        if not self.current_repo or self.current_index is None:
            return
        if self.current_index <= 0:
            return
        self.load_repo(self.current_repo, self.current_index - 1)

    def next_commit(self):
        if not self.current_repo or self.current_index is None or not self.current_data:
            return
        if self.current_index >= len(self.current_data["commits"]) - 1:
            return
        self.load_repo(self.current_repo, self.current_index + 1)


if __name__ == "__main__":
    UniversalGitAnalyzerApp()
