import asyncio
import os
from typing import Callable, List, Optional

import carb.settings
import omni.ui as ui

from .thumbnail_grid import ThumbnailGrid, ThumbnailItem, ThumbnailModel

SETTING_RECENT_FILES = "/persistent/app/file/recentFiles"

class RecentItem(ThumbnailItem):
    """
    Data item for recent file.
    """
    def __init__(self, url: str):
        super().__init__(self.__get_thumbnail(url), url)

    def __get_thumbnail(self, url: str):
        file_name = os.path.basename(url)
        path = os.path.dirname(url)

        # Do not check if thumbnail image exists here, leave it to ThumbnailGrid
        return f"{path}/.thumbs/256x256/{file_name}.png"

    def execute(self, url: Optional[str] = None):
        from .open_action import run_open_action
        run_open_action("omni.kit.window.file", "open_stage", url if url else self.url)


class RecentModel(ThumbnailModel):
    """
    Data model for recent files.
    """
    def __init__(self, recent_files: List[str]):
        super().__init__([])
        self.refresh(recent_files)

    def refresh(self, recent_files: List[str]) -> None:
        self._items = [RecentItem(recent) for recent in recent_files] if recent_files else []
        self._item_changed(None)


class RecentWidget:
    """
    Show recents grid and open widgets.
    """
    def __init__(self, on_recent_changed_fn: Callable[[Optional[ui.AbstractItem]], None], on_grid_mode_fn: Callable[[bool], None]):
        self._settings = carb.settings.get_settings()
        self.__get_recent_history()
        self._grid_model = RecentModel(self.__recent_files)
        self._recent_table: Optional[RecentTable] = None
        self.__on_recent_selected_fn = on_recent_changed_fn
        self.__on_grid_mode_fn = on_grid_mode_fn
        self._build_ui()

    def destroy(self):
        self._subscription = None
        self._event_sub = None

    @property
    def selected_url(self) -> str:
        if self._grid_frame.visible:
            return self._recent_grid.selected.url if self._recent_grid.selected else ""
        else:
            return self._recent_table.selected
        
    @selected_url.setter
    def selected_url(self, url: str) -> None:
        if self._grid_frame.visible:
            self._recent_grid.select_url(url)
        else:
            self._recent_table.selected = url

    def clear_selection(self):
        if self._grid_frame.visible:
            self._recent_grid.selected = None
        else:
            self._recent_table.selected = None

    def show_grid(self, grid: bool) -> None:
        self._grid_frame.visible = grid
        self._table_frame.visible = not grid

        if grid:
            # Set grid selection from table
            if self._recent_table:
                self._recent_grid.select_url(self._recent_table.selected)
        else:
            # Set table selection from grid
            if self._recent_table:
                if self._recent_grid.selected:
                    self._recent_table.selected = self._recent_grid.selected.url
            else:
                # Wait for widget create and set selection
                async def __delay_select():
                    await omni.kit.app.get_app().next_update_async()
                    if self._recent_grid.selected:
                        self._recent_table.selected = self._recent_grid.selected.url

                asyncio.ensure_future(__delay_select())

        self.__on_grid_mode_fn(grid)

    def open_selected(self, checkpoint_url: Optional[str]) -> None:
        def __open_file(url):
            carb.log_info(f"Open recent file: {url}")
            from .open_action import run_open_action
            run_open_action("omni.kit.window.file", "open_stage", url)
            
        if self.selected_url:
            if self._grid_frame.visible:
                __open_file(checkpoint_url or self.selected_url)
            else:
                __open_file(self._recent_table.get_filename())

    def _build_ui(self):
        with ui.ZStack():
            self._grid_frame = ui.Frame(build_fn=self._build_grid_ui)
            self._table_frame = ui.Frame(build_fn=self._build_table_ui, visible=False)

    def _build_table_ui(self):
        try:
            from .recent_table import RecentTable
            self._recent_table = RecentTable(self.__recent_files, on_selection_changed_fn=self.__on_recent_selected_fn)
        except Exception as e:
            self._recent_table = None
            carb.log_error(f"Failed to create recent table: {e}")

    def _build_grid_ui(self):
        with ui.VStack():
            # Recent grid
            with ui.HStack():
                ui.Spacer(width=8)
                self._recent_grid = ThumbnailGrid(self._grid_model, on_selection_changed=self.__on_recent_selected_fn)
                ui.Spacer(width=8)

    def _on_change(self, item, event_type):
        if event_type == carb.settings.ChangeEventType.CHANGED:
            self.__recent_files = self._settings.get(SETTING_RECENT_FILES)
            self.__rebuild_recents()

    def __rebuild_recents_from_event(self) -> None:
        from omni.kit.helper.file_utils import get_latest_urls_from_event_queue, asset_types
        self.__recent_files = get_latest_urls_from_event_queue(self._max_recent_files, asset_type=asset_types.ASSET_TYPE_USD)
        if self._recent_table:
            self._recent_table.refresh(self.__recent_files)
        self._grid_model.refresh(self.__recent_files)

    def __get_recent_history(self):
        load_recents_from_event = self._settings.get("/exts/omni.kit.welcome.open/load_recent_from_event")
        if load_recents_from_event:
            self.__get_recent_from_event()
        else:
            self.__recent_files = self._settings.get(SETTING_RECENT_FILES)
            # If recent files changed, refresh
            self._subscription = omni.kit.app.SettingChangeSubscription(SETTING_RECENT_FILES, self._on_change)

    def __get_recent_from_event(self) -> None:
        import omni.kit.app
        manager = omni.kit.app.get_app().get_extension_manager()
        manager.set_extension_enabled_immediate("omni.kit.helper.file_utils", True)
        from omni.kit.helper.file_utils import get_latest_urls_from_event_queue, asset_types, FILE_EVENT_QUEUE_UPDATED
        self._max_recent_files = self._settings.get("exts/omni.kit.menu.file/maxRecentFiles") or 10
        self.__recent_files = get_latest_urls_from_event_queue(self._max_recent_files, asset_type=asset_types.ASSET_TYPE_USD)

        event_stream = omni.kit.app.get_app().get_message_bus_event_stream()
        self._event_sub = event_stream.create_subscription_to_pop_by_type(FILE_EVENT_QUEUE_UPDATED, lambda _: self.__rebuild_recents_from_event())