# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: GPL-3.0-only


import os
import platform
import typing
from dataclasses import dataclass
from datetime import datetime as dt
from pathlib import Path

import structlog
from humanfriendly import format_size  # pyright: ignore[reportUnknownVariableType]
from PIL import ImageFont
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget

from tagstudio.core.enums import ShowFilepathOption
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.ignore import Ignore
from tagstudio.core.media_types import MediaCategories
from tagstudio.core.utils.str_formatting import format_duration
from tagstudio.core.utils.types import unwrap
from tagstudio.i18n.translations import Translations
from tagstudio.qt.controllers.file_opener_label import FileOpenerLabel
from tagstudio.qt.utils.file_opener import FileOpenerHelper
from tagstudio.qt.views.styles.palette import ColorType, UiColor, get_ui_color
from tagstudio.qt.views.styles.stylesheets import properties_style

if typing.TYPE_CHECKING:
    from tagstudio.qt.qt_driver import QtDriver

logger = structlog.get_logger(__name__)


@dataclass
class FileAttributeData:
    width: int | None = None
    height: int | None = None
    duration: int | None = None


# TODO: Split to use MVC guidelines.
class FileAttributes(QWidget):
    def __init__(self, library: Library, driver: QtDriver):
        super().__init__()
        root_layout = QVBoxLayout(self)
        root_layout.setContentsMargins(0, 0, 0, 0)
        root_layout.setSpacing(0)

        self.date_style = "font-size: 12px;"
        self.file_label_style = "font-size: 12px"

        self.file_label = FileOpenerLabel()
        self.file_label.setObjectName("filenameLabel")
        self.file_label.setTextFormat(Qt.TextFormat.RichText)
        self.file_label.setWordWrap(True)
        self.file_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
        self.file_label.setStyleSheet(self.file_label_style)

        self.date_created_label = QLabel()
        self.date_created_label.setObjectName("dateCreatedLabel")
        self.date_created_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.date_created_label.setTextFormat(Qt.TextFormat.RichText)
        self.date_created_label.setStyleSheet(self.date_style)
        self.date_created_label.setHidden(True)

        self.date_modified_label = QLabel()
        self.date_modified_label.setObjectName("dateModifiedLabel")
        self.date_modified_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.date_modified_label.setTextFormat(Qt.TextFormat.RichText)
        self.date_modified_label.setStyleSheet(self.date_style)
        self.date_modified_label.setHidden(True)

        self.dimensions_label = QLabel()
        self.dimensions_label.setObjectName("dimensionsLabel")
        self.dimensions_label.setWordWrap(True)
        self.dimensions_label.setStyleSheet(properties_style())
        self.dimensions_label.setHidden(True)

        self.date_container = QWidget()
        date_layout = QVBoxLayout(self.date_container)
        date_layout.setContentsMargins(0, 2, 0, 0)
        date_layout.setSpacing(0)
        date_layout.addWidget(self.date_created_label)
        date_layout.addWidget(self.date_modified_label)

        root_layout.addWidget(self.file_label)
        root_layout.addWidget(self.date_container)
        root_layout.addWidget(self.dimensions_label)
        self.library = library
        self.driver = driver

    def update_date_label(self, filepath: Path | None = None) -> None:
        """Update the "Date Created" and "Date Modified" file property labels."""
        if filepath and filepath.is_file():
            created: dt
            if platform.system() == "Windows" or platform.system() == "Darwin":
                # NOTE: Accessing stat().st_birthtime causes linter checks to fail on some systems.
                created = dt.fromtimestamp(filepath.stat().st_birthtime)  # type: ignore[attr-defined, unused-ignore]
            else:
                created = dt.fromtimestamp(filepath.stat().st_ctime)
            modified: dt = dt.fromtimestamp(filepath.stat().st_mtime)
            self.date_created_label.setText(
                f"<b>{Translations['file.date_created']}:</b>"
                + f" {self.driver.settings.format_datetime(created)}"
            )
            self.date_modified_label.setText(
                f"<b>{Translations['file.date_modified']}:</b> "
                f"{self.driver.settings.format_datetime(modified)}"
            )
            self.date_created_label.setHidden(False)
            self.date_modified_label.setHidden(False)
        elif filepath:
            self.date_created_label.setText(
                f"<b>{Translations['file.date_created']}:</b> <i>N/A</i>"
            )
            self.date_modified_label.setText(
                f"<b>{Translations['file.date_modified']}:</b> <i>N/A</i>"
            )
            self.date_created_label.setHidden(False)
            self.date_modified_label.setHidden(False)
        else:
            self.date_created_label.setHidden(True)
            self.date_modified_label.setHidden(True)

    def update_stats(self, filepath: Path | None = None, stats: FileAttributeData | None = None):
        """Render the panel widgets with the newest data from the Library."""
        if not stats:
            stats = FileAttributeData()

        if not filepath:
            self.layout().setSpacing(0)  # pyright: ignore[reportOptionalMemberAccess]
            self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            self.file_label.setText(f"<i>{Translations['preview.no_selection']}</i>")
            self.file_label.set_file_path(Path())
            self.file_label.setCursor(Qt.CursorShape.ArrowCursor)
            self.dimensions_label.setText("")
            self.dimensions_label.setHidden(True)
        else:
            ext = filepath.suffix.lower()
            display_path = filepath
            if self.driver.settings.show_filepath == ShowFilepathOption.SHOW_FULL_PATHS:
                display_path = filepath
            elif self.driver.settings.show_filepath == ShowFilepathOption.SHOW_RELATIVE_PATHS:
                display_path = Path(filepath).relative_to(unwrap(self.library.library_dir))
            elif self.driver.settings.show_filepath == ShowFilepathOption.SHOW_FILENAMES_ONLY:
                display_path = Path(filepath.name)

            self.layout().setSpacing(6)  # pyright: ignore[reportOptionalMemberAccess]
            self.file_label.setAlignment(Qt.AlignmentFlag.AlignLeft)
            self.file_label.set_file_path(filepath)
            self.dimensions_label.setHidden(False)

            file_str: str = ""
            separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>"  # Gray
            for i, part in enumerate(display_path.parts):
                part_ = part.strip(os.path.sep)
                if i != len(display_path.parts) - 1:
                    file_str += f"{'\u200b'.join(part_)}{separator}</b>"
                else:
                    if file_str != "":
                        file_str += "<br>"
                    file_str += f"<b>{'\u200b'.join(part_)}</b>"
            self.file_label.setText(file_str)
            self.file_label.setCursor(Qt.CursorShape.PointingHandCursor)
            self.opener = FileOpenerHelper(filepath)

            # Initialize the possible stat variables
            stats_label_text = ""
            ext_display: str = ""
            file_size: str = ""
            font_family: str = ""

            # Attempt to populate the stat variables
            ext_display = ext.upper()[1:] or filepath.stem.upper()
            if filepath and filepath.is_file():
                try:
                    file_size = format_size(filepath.stat().st_size)

                    if MediaCategories.is_ext_in_category(
                        ext, MediaCategories.FONT_TYPES, mime_fallback=True
                    ):
                        font = ImageFont.truetype(filepath)
                        font_family = f"{font.getname()[0]} ({font.getname()[1]}) "
                except (FileNotFoundError, OSError) as e:
                    logger.error(
                        "[FileAttributes] Could not process file stats", filepath=filepath, error=e
                    )

            # Format and display any stat variables
            def add_newline(stats_label_text: str) -> str:
                if stats_label_text and stats_label_text[-4:] != "<br>":
                    return stats_label_text + "<br>"
                return stats_label_text

            if ext_display:
                stats_label_text += ext_display
                red = get_ui_color(ColorType.PRIMARY, UiColor.RED)
                orange = get_ui_color(ColorType.PRIMARY, UiColor.ORANGE)

                if Ignore.compiled_patterns and Ignore.compiled_patterns.match(
                    filepath.relative_to(unwrap(self.library.library_dir))
                ):
                    stats_label_text = (
                        f"{stats_label_text}"
                        f"  •  <span style='color:{orange}'>"
                        f"{Translations['preview.ignored'].upper()}</span>"
                    )
                if not filepath.exists():
                    stats_label_text = (
                        f"{stats_label_text}"
                        f"  •  <span style='color:{red}'>"
                        f"{Translations['preview.unlinked'].upper()}</span>"
                    )
                if file_size:
                    stats_label_text += f"  •  {file_size}"
            elif file_size:
                stats_label_text += file_size

            if stats.width is not None and stats.height is not None:
                stats_label_text = add_newline(stats_label_text)
                stats_label_text += f"{stats.width} x {stats.height} px"

            if stats.duration is not None:
                stats_label_text = add_newline(stats_label_text)
                stats_label_text += format_duration(stats.duration)

            if font_family:
                stats_label_text = add_newline(stats_label_text)
                stats_label_text += f"{font_family}"

            self.dimensions_label.setText(stats_label_text)

    def update_multi_selection(self, count: int):
        """Format attributes for multiple selected items."""
        self.layout().setSpacing(0)  # pyright: ignore[reportOptionalMemberAccess]
        self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.file_label.setText(Translations.format("preview.multiple_selection", count=count))
        self.file_label.setCursor(Qt.CursorShape.ArrowCursor)
        self.file_label.set_file_path(Path())
        self.dimensions_label.setText("")
        self.dimensions_label.setHidden(True)
