# ws_package/services/screenshot_service.py

from ws_package.repositories.screenshot_repo import screenshot_manager
from ws_package.entities.screenshot_entity import ScreenshotEntity
from ws_package.entities.user_entity import UserEntity
from ws_package.log.writer import LogWriter
from ws_package.utils.s3 import S3sync
from ws_package.utils.s3 import s3_detail_for_upload
from ws_package.exceptions.custom_exceptions import ScreenshotError, NetworkError, ValidationError

logger = LogWriter.file(__name__)

class ScreenshotService:
    def __init__(self):
        # Instance-specific state (converted from class-level)
        self.org_id = None
    
    async def save(self, screenshot_entity: ScreenshotEntity):
        saved = await screenshot_manager.save_screenshot(screenshot_entity)
        if saved is not None and saved is not False:
            # Extract attributes properly to avoid SQLAlchemy internal state (_sa_instance_state)
            return ScreenshotEntity(
                file_name=saved.file_name,
                file_location=saved.file_location,
                file_size=saved.file_size,
                s3_upload_link=saved.s3_upload_link,
                is_uploaded=saved.is_uploaded,
                blur=saved.blur,
                is_s3_uploaded=saved.is_s3_uploaded,
                counter=saved.counter
            )
        return None
                  
    async def update(self, screenshot):
        updated = await screenshot_manager.update_screenshot(screenshot)
        if updated is not None and updated is not False:
            # Extract attributes properly to avoid SQLAlchemy internal state (_sa_instance_state)
            return ScreenshotEntity(
                file_name=updated.file_name,
                file_location=updated.file_location,
                file_size=updated.file_size,
                s3_upload_link=updated.s3_upload_link,
                is_uploaded=updated.is_uploaded,
                blur=updated.blur,
                is_s3_uploaded=updated.is_s3_uploaded,
                counter=updated.counter
            )
        return None
  
    async def sync(self, src, dest, user_entity: UserEntity):
        """Upload screenshot to S3 with improved error handling."""
        if not src or not dest:
            raise ValidationError("Source and destination paths are required", 
                                context={"src": src, "dest": dest})
        
        if not user_entity:
            raise ValidationError("User entity is required for upload", 
                                context={"user_entity": str(user_entity)})
        
        try:
            # Validate org_id is set
            if not self.org_id:
                raise ValidationError("Organization ID not set in ScreenshotService", 
                                    context={"org_id": self.org_id})
            
            detail = await s3_detail_for_upload(self.org_id, user_entity)
            if not detail:
                raise ScreenshotError("Failed to get S3 configuration details", 
                                    context={"org_id": self.org_id})
            
            s3_sync = S3sync(detail)
            
            if user_entity.bucket == "hetzner":
                logger.info("Uploading screenshot to Hetzner bucket")
                result = await s3_sync.upload_file_to_hetzner(src, dest)
            else:
                logger.info("Uploading screenshot to AWS bucket")
                result = await s3_sync.upload_file(src, dest)
            
            if not result:
                raise ScreenshotError("Upload failed - no result returned", 
                                    context={"src": src, "dest": dest, "bucket": user_entity.bucket})
            
            logger.info("Screenshot uploaded successfully: %s", dest)
            return result
            
        except ValidationError:
            # Re-raise validation errors as-is
            raise
        except ScreenshotError:
            # Re-raise screenshot errors as-is
            raise
        except NetworkError:
            # Re-raise network errors as-is
            raise
        except Exception as e:
            error_message = str(e)
            
            # S3 clock skew (RequestTimeTooSkewed) — not a timezone mismatch; caller skips upload and continues.
            if "S3_TIME_TAMPERING:" in error_message or "RequestTimeTooSkewed" in error_message:
                logger.warning(
                    "Screenshot upload skipped: S3 RequestTimeTooSkewed (clock skew): %s",
                    error_message,
                )
                raise ScreenshotError(
                    f"S3 clock skew: {error_message}",
                    context={"src": src, "dest": dest, "error": error_message},
                )
            
            logger.error("Unexpected error in syncing screenshot: %s", e)
            raise ScreenshotError(f"Unexpected error during screenshot upload: {e}", 
                                context={"src": src, "dest": dest, "error": str(e)})

    async def retry_pending_uploads(self, bucket: str) -> int:
        """Retry screenshots that failed to upload (e.g. were taken while offline).

        Queries the DB for rows where is_uploaded=False, attempts to re-upload each
        one using the cached S3 credentials, and marks successes.  Call this once
        after S3 credentials are confirmed available (e.g. post-auth prefetch).

        Args:
            bucket: "hetzner" or "aws" — determines which upload path to use.

        Returns:
            Number of screenshots successfully re-uploaded.
        """
        if not self.org_id:
            return 0

        try:
            pending = await screenshot_manager.get_pending_uploads()
        except Exception as e:
            logger.warning("Could not fetch pending screenshot uploads: %s", e)
            return 0

        if not pending:
            return 0

        logger.info("Retrying %d pending screenshot upload(s)...", len(pending))

        try:
            from ws_package.repositories.user_repo import user_manager

            user_entity = await user_manager.get_user()
            detail = await s3_detail_for_upload(self.org_id, user_entity)
        except Exception as e:
            logger.warning("Could not get S3 credentials for retry: %s", e)
            return 0

        if not detail:
            return 0

        s3_sync = S3sync(detail)
        succeeded = 0

        for row in pending:
            src = getattr(row, "file_location", None)
            dest = getattr(row, "file_name", None)
            row_id = getattr(row, "id", None)

            if not src or not dest or not row_id:
                continue

            import os
            if not os.path.isfile(src):
                logger.debug("Pending screenshot file missing, skipping: %s", src)
                continue

            try:
                if bucket == "hetzner":
                    result = await s3_sync.upload_file_to_hetzner(src, dest)
                else:
                    result = await s3_sync.upload_file(src, dest)

                if result:
                    await screenshot_manager.mark_uploaded(row_id)
                    succeeded += 1
                    logger.info("Retried upload succeeded: %s", dest)
            except Exception as e:
                logger.warning("Retry upload failed for %s: %s", dest, e)

        if succeeded:
            logger.info("Pending screenshot retry: %d/%d succeeded", succeeded, len(pending))

        return succeeded

    async def sync_on_demand(self, src, dest, user_entity: UserEntity):
        """Upload an on-demand screenshot to S3 (no DB save, direct upload)."""
        try:
            if not self.org_id:
                raise ValidationError("Organization ID not set in ScreenshotService",
                                    context={"org_id": self.org_id})

            detail = await s3_detail_for_upload(self.org_id, user_entity)
            if not detail:
                raise ScreenshotError("Failed to get S3 configuration details",
                                    context={"org_id": self.org_id})

            s3_sync = S3sync(detail)

            if user_entity.bucket == "hetzner":
                logger.info("Uploading on-demand screenshot to Hetzner bucket")
                result = await s3_sync.upload_file_to_hetzner(src, dest)
            else:
                logger.info("Uploading on-demand screenshot to AWS bucket")
                result = await s3_sync.upload_file_on_demand(src, dest)

            return result

        except Exception as e:
            logger.error("Error uploading on-demand screenshot to S3: %s", e, exc_info=True)
            return src