# Copyright (C) 2025 Alexander Vanhee
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later

import sys
import os
import tempfile
import shutil

from collections.abc import Sequence
from typing import Optional

from PIL import Image

from gi.repository import Adw, Gio, Xdp

from gradia.constants import app_id, rootdir  # pyright: ignore
from gradia.ui.window import GradiaMainWindow
from gradia.ui.dialog.ocr_launcher import present_ocr_dialog
from gradia.backend.logger import Logger
from gradia.backend.ocr import OCR
from gradia.utils.std_image_loader import StdinImageLoader

logging = Logger()


class GradiaApp(Adw.Application):
    __gtype_name__ = "GradiaApp"

    def __init__(self, version: str):
        super().__init__(
            application_id=app_id,
            flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE | Gio.ApplicationFlags.HANDLES_OPEN
        )

        self.set_resource_base_path(rootdir)

        self.version = version
        self.temp_dirs: list[str] = []
        self._stdin_image_path: Optional[str] = None


        self.connect("shutdown", self.on_shutdown)

    def do_command_line(self, command_line: Gio.ApplicationCommandLine) -> int:
        args = command_line.get_arguments()[1:]
        logging.info(f"Command line arguments: {args}")

        if "--help" in args or "-h" in args:
            self._print_help()
            return 0

        files_to_open = []
        screenshot_file = None
        ocr_file = None

        for arg in args:
            if arg.startswith("--screenshot-file="):
                screenshot_file = arg.split("=", 1)[1]
                logging.info(f"Screenshot file detected: {screenshot_file}")
            elif arg.startswith("--ocr-file="):
                ocr_file = arg.split("=", 1)[1]
                logging.info(f"OCR file detected: {ocr_file}")
            elif not arg.startswith("--"):
                try:
                    file = Gio.File.new_for_commandline_arg(arg)
                    path = file.get_path()
                    if path:
                        files_to_open.append(path)
                        logging.debug(f"File to open detected: {path}")
                    else:
                        logging.warning(f"Argument {arg} does not have a valid path.")
                except Exception as e:
                    logging.warning(f"Failed to parse file URI {arg}.", exception=e, show_exception=True)

        if ocr_file:
            self._open_ocr_standalone(ocr_file)
        elif files_to_open:
            for path in files_to_open:
                self._open_window(file_path=path)
        elif screenshot_file:
            self._open_window(start_screenshot=screenshot_file)
        else:
            self.activate()

        return 0

    def _print_help(self):
        file = Gio.File.new_for_uri("resource:///be/alexandervanhee/gradia/help.txt")
        stream = file.read(None)
        contents = stream.read_bytes(4096, None).get_data().decode("utf-8")
        print(contents)

    def do_open(self, files: Sequence[Gio.File], hint: str):
        logging.debug(f"do_open called with files: {[file.get_path() for file in files]} and hint: {hint}")
        for file in files:
            path = file.get_path()
            if path:
                self._open_window(path)

    def do_activate(self):
        logging.debug("do_activate called")

        stdin_path = self._stdin_image_path
        self._stdin_image_path = None

        if stdin_path:
            logging.debug(f"Opening window with stdin image path: {stdin_path}")
            self._open_window(stdin_path)
        else:
            self._open_window(None)

    def _open_window(
        self,
        file_path: Optional[str] = None,
        start_screenshot: Optional[str] = None,
    ):
        logging.info(f"Opening window with file_path={file_path}")
        temp_dir = tempfile.mkdtemp()
        logging.debug(f"Created temp directory: {temp_dir}")
        self.temp_dirs.append(temp_dir)

        window = GradiaMainWindow(
            temp_dir=temp_dir,
            version=self.version,
            application=self,
            file_path=file_path,
            start_screenshot=start_screenshot,
        )
        window.show()

    def _open_ocr_standalone(self, image_path: str) -> None:
        logging.info(f"Opening standalone OCR dialog for: {image_path}")

        if not os.path.isfile(image_path):
            logging.warning(f"OCR file does not exist: {image_path}")
            return

        try:
            pil_image = Image.open(image_path)
            pil_image.load()
        except Exception as e:
            logging.warning(f"Failed to load image for OCR: {image_path}", exception=e, show_exception=True)
            return

        self.hold()
        released = {"done": False}

        def release_once():
            if not released["done"]:
                released["done"] = True
                self.release()

        present_ocr_dialog(
            pil_image,
            parent=None,
            on_dialog_shown=lambda dialog: dialog.connect("closed", lambda *_: release_once()),
            on_cancelled=release_once,
            auto_copy=True,
        )

    def on_shutdown(self, application):
        logging.info("Application shutdown started, cleaning temp directories…")
        for temp_dir in self.temp_dirs:
            try:
                if os.path.exists(temp_dir):
                    shutil.rmtree(temp_dir)
                    logging.debug(f"Deleted temp dir: {temp_dir}")
            except Exception as e:
                logging.warning(f"Failed to clean up temp dir {temp_dir}.", exception=e, show_exception=True)
        logging.info("Cleanup complete.")

def main(version: str) -> int:
    try:
        logging.info("Application starting…")
        loader = StdinImageLoader()
        image_path = loader.read_from_stdin()

        app = GradiaApp(version=version)
        app._stdin_image_path = image_path

        return app.run(sys.argv)

    except Exception as e:
        logging.critical("Application closed with an exception.", exception=e, show_exception=True)
        return 1
