"""
ActivityScreenshotMixin — screenshot capture, S3 upload, and URL validation.

Provides screenshot handling for :class:`~ws_package.controllers.activity_controller.ActivityController`.
All methods assume ``self`` is an ``ActivityController`` instance and have access
to all attributes defined in ``ActivityController.__init__``.

Thread-safety:
    ``ss()`` and ``_capture_stop_screenshot()`` are async and must be awaited from the
    async monitoring loop.  ``is_link()`` and ``forward_ss()`` are synchronous and
    thread-safe as they perform no shared-state mutation.
"""
from __future__ import annotations

import datetime
import os
import asyncio
from typing import Optional

from ws_package.log.writer import LogWriter
from ws_package.exceptions.custom_exceptions import (
    ValidationError, APIError, NetworkError, ScreenshotError
)
from ws_package.entities.screenshot_entity import ScreenshotEntity

logger = LogWriter.file(__name__)


class ActivityScreenshotMixin:
    """Screenshot capture, S3 upload and validation for ActivityController."""

    def is_link(self, url):
        """Check if the given string is a valid HTTP/HTTPS URL."""
        return self.screenshot_manager.is_link(url)

    def forward_ss(self, ss: str) -> str:
        """Normalise a screenshot path to a forward-slash basename for S3 key construction.

        Args:
            ss: Local file path (may use backslashes on Windows).

        Returns:
            Basename of the path with all backslashes replaced by forward slashes.
        """
        return self.screenshot_manager.forward_ss(ss)

    async def ss(self, interval_start_time, interval_end_time, user_entity):
        """Validate screenshots and upload local files to S3; return list of S3 URLs."""
        ss_array = await self.activity_service.get_screenshots(interval_start_time, interval_end_time)
        ans_array = []
        for ss in ss_array:
            if self.is_link(ss):
                ans_array.append(ss)

            else:
                try:
                    # Validate screenshot file exists and is accessible
                    if not os.path.exists(ss):
                        logger.warning("Screenshot file not found: %s", ss)
                        continue

                    if not os.access(ss, os.R_OK):
                        logger.warning("Screenshot file not readable: %s", ss)
                        continue

                    try:
                        task = asyncio.create_task(
                            self.screenshot_controller.screenshot_service.sync(
                                ss,
                                f"{user_entity.org_id}/{user_entity.user_id}/"
                                f"{self.s3_screenshot_path}{self.screenshot_controller.format_ss_name(self.forward_ss(ss))}/"
                                f"{self.get_os_type()}/{self.screenshot_controller.unix_format_ss_name(self.forward_ss(ss))}",
                                user_entity
                            ),
                        )
                        # Track task to prevent silent failures
                        self._background_tasks.add(task)
                        task.add_done_callback(self._background_tasks.discard)

                        response = await task
                        s3_upload_link = response
                        ans_array.append(s3_upload_link)
                        # Delete local file once we have AWS link - no need to wait for next interval
                        if s3_upload_link and ss:
                            try:
                                screenshot_dir = os.path.dirname(ss)
                                screenshot_name = os.path.basename(ss)
                                self.screenshot_controller.delete_screenshot(screenshot_name, screenshot_dir)
                            except Exception as del_e:
                                logger.warning("Failed to delete screenshot file after upload: %s", del_e)
                    except ValidationError as e:
                        logger.error("Screenshot sync validation error for screenshot: %s", e, exc_info=True)
                        logger.error("Context: file=%s, dest=%s%s", ss, self.s3_screenshot_path, self.screenshot_controller.format_ss_name(self.forward_ss(ss)))
                        # Skip this screenshot and continue processing
                        continue
                    except ScreenshotError as e:
                        error_message = str(e)
                        # Check for S3 time tampering error (S3_TIME_TAMPERING or legacy message)
                        if "S3_TIME_TAMPERING" in error_message or "Time tampering detected" in error_message or "RequestTimeTooSkewed" in error_message:
                            logger.error("S3 time tampering detected during screenshot upload: %s", error_message)
                            self.handle_s3_time_error(error_message)
                            # Continue processing other screenshots
                            continue
                        logger.error("Screenshot sync error for screenshot: %s", e, exc_info=True)
                        logger.error("Context: file=%s, dest=%s%s", ss, self.s3_screenshot_path, self.screenshot_controller.format_ss_name(self.forward_ss(ss)))
                        # Skip this screenshot and continue processing
                        continue
                    except NetworkError as e:
                        logger.error("Network error during screenshot sync for screenshot: %s", e, exc_info=True)
                        logger.error("Context: file=%s, dest=%s%s", ss, self.s3_screenshot_path, self.screenshot_controller.format_ss_name(self.forward_ss(ss)))
                        # Skip this screenshot and continue processing
                        continue
                    except Exception as e:
                        error_message = str(e)
                        if "S3_TIME_TAMPERING" in error_message or "RequestTimeTooSkewed" in error_message:
                            logger.error("S3 time tampering detected during screenshot upload: %s", error_message)
                            self.handle_s3_time_error(error_message)
                        else:
                            logger.error("Unexpected error during screenshot sync for screenshot: %s", e, exc_info=True)
                            logger.error("Context: file=%s, dest=%s%s", ss, self.s3_screenshot_path, self.screenshot_controller.format_ss_name(self.forward_ss(ss)))
                        # Skip this screenshot and continue processing
                        continue

                    # Extract timestamp with error handling
                    try:
                        particular_timestamp = self.screenshot_controller.extract_datetime_from_filename(ss)
                    except Exception as e:
                        logger.warning("Failed to extract timestamp from filename %s: %s", ss, e)
                        particular_timestamp = datetime.datetime.now()

                    screenshot_entity = ScreenshotEntity(
                        file_name=ss,
                        blur=True,
                        s3_upload_link=s3_upload_link,
                        file_size=str(0000),
                        is_uploaded=False,
                        file_location=ss,
                        timestamp=particular_timestamp,
                        is_s3_uploaded=True,
                        counter=self.last_count+1
                    )

                except FileNotFoundError as e:
                    logger.warning("Screenshot file not found: %s - %s", ss, e)
                    continue
                except PermissionError as e:
                    logger.warning("Permission denied accessing screenshot: %s - %s", ss, e)
                    continue
                except OSError as e:
                    logger.warning("OS error accessing screenshot: %s - %s", ss, e)
                    continue
                except APIError as e:
                    logger.error("API error uploading screenshot %s: %s", ss, e, exc_info=True)
                    continue
                except NetworkError as e:
                    logger.error("Network error uploading screenshot %s: %s", ss, e, exc_info=True)
                    continue
                except ValidationError as e:
                    logger.error("Validation error with screenshot %s: %s", ss, e, exc_info=True)
                    continue
                except Exception as e:
                    logger.error("Unexpected error uploading screenshot %s: %s", ss, e, exc_info=True)
                    continue

        return ans_array

    async def _capture_stop_screenshot(self, user_entity) -> None:
        """
        On stop button click: capture one screenshot, upload to AWS, save to DB.
        The screenshot is saved with timestamp=now and counter=last_count+1 so it is
        included in the last activity interval when intervals are sent (get_screenshots by time range).
        """
        try:
            if getattr(self.screenshot_controller, 'screenshot_interval', 0) <= 0:
                logger.debug("Stop: screenshot capture disabled - skipping stop screenshot")
                return
            capture_time = datetime.datetime.now()
            counter = self.last_count + 1
            self.screenshot_controller.last_count = self.last_count
            await self.screenshot_controller.capture_stop_screenshot(user_entity, counter, capture_time)
            # Flush queue so the stop screenshot is committed before last_api_call (get_screenshots will then include it)
            try:
                from ws_package.services.db_writer_service import db_writer_service
                await db_writer_service.process_immediately()
                await asyncio.sleep(0.5)  # Allow DB commit to be visible before last_api_call queries screenshots
            except Exception as e:
                logger.warning("Stop: flush after stop screenshot failed: %s", e)
        except Exception as e:
            logger.warning("Stop: screenshot capture failed (non-fatal): %s", e, exc_info=True)

    async def _upload_file_to_s3(self, local_file_path: str) -> Optional[str]:
        """
        Upload a file to S3 and return the S3 URL.

        Args:
            local_file_path: Local file path to upload

        Returns:
            S3 URL if successful, None otherwise
        """
        try:
            # Get user_entity if not available
            user_entity = None
            if hasattr(self, 'user_entity') and self.user_entity is not None:
                user_entity = self.user_entity

            if user_entity is None:
                logger.error("Cannot upload file to S3: user_entity is not available and cannot be retrieved from database")
                return None

            # Get org_id from user_entity
            org_id = user_entity.org_id
            if org_id is None:
                logger.error("Cannot upload file to S3: org_id is not set in user_entity")
                return None

            logger.info("Using user_entity with org_id: %s, user_id: %s", org_id, user_entity.user_id)

            # Check if file exists
            import os
            if not os.path.exists(local_file_path):
                logger.error("Cannot upload file to S3: file does not exist: %s", local_file_path)
                return None

            # Get S3 configuration
            from ws_package.utils.s3 import s3_detail_for_upload, S3sync
            s3_config = await s3_detail_for_upload(org_id, user_entity)
            if not s3_config:
                logger.error("Cannot upload file to S3: S3 configuration not found for org_id: %s", org_id)
                return None

            # Create S3sync instance
            s3_sync = S3sync(s3_config)

            # Generate S3 destination path: notes/{org_id}/{user_id}/{timestamp}-{filename}
            file_name = os.path.basename(local_file_path)
            timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
            s3_dest_path = f"notes/{org_id}/{user_entity.user_id}/{timestamp}-{file_name}"

            logger.info("Uploading file to S3: %s -> %s", local_file_path, s3_dest_path)

            # Upload based on bucket type
            if user_entity.bucket == "hetzner":
                logger.info("Uploading to Hetzner bucket")
                s3_url = await s3_sync.upload_file_to_hetzner(local_file_path, s3_dest_path)
            else:
                logger.info("Uploading to AWS bucket")
                s3_url = await s3_sync.upload_file(local_file_path, s3_dest_path)

            if s3_url:
                logger.info("File uploaded successfully to S3: %s", s3_url)
                return s3_url
            else:
                logger.error("S3 upload returned no URL for file: %s", local_file_path)
                return None
        except Exception as e:
            logger.error("Error uploading file to S3: %s", e, exc_info=True)
            return None
