"""object 命令 - S3 Object 级别操作

覆盖 boto3 S3 的所有 object 级别操作:
list, get, put, delete, copy, move, head, presign,
restore, tagging, acl, versions, multipart
"""
import os
import json
from typing import Optional
from pathlib import Path

from rich.table import Table
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, FileSizeColumn, TransferSpeedColumn
from botocore.exceptions import ClientError

from .client import get_s3_client
from .utils import (
    console, err_console, format_size, format_datetime,
    print_json, handle_error
)


class Object:
    """S3 Object 管理命令"""

    # ── 列举 ─────────────────────────────────────────────────────

    def list(
        self,
        bucket: str,
        prefix: str = "",
        delimiter: str = "",
        max_keys: int = 1000,
        format: str = "table",
    ) -> None:
        """列出 bucket 中的对象

        Args:
            bucket:    Bucket 名称
            prefix:    前缀过滤（默认空，列出全部）
            delimiter: 分���符（使用 / 模拟目录结构）
            max_keys:  最大返回数量（默认 1000）
            format:    输出格式，table 或 json（默认 table）

        示例:
            s3c object list my-bucket
            s3c object list my-bucket --prefix folder/
            s3c object list my-bucket --prefix folder/ --delimiter /
        """
        try:
            s3 = get_s3_client()
            kwargs: dict = {"Bucket": bucket, "MaxKeys": max_keys}
            if prefix:
                kwargs["Prefix"] = prefix
            if delimiter:
                kwargs["Delimiter"] = delimiter

            all_objects = []
            all_prefixes = []
            paginator = s3.get_paginator("list_objects_v2")
            for page in paginator.paginate(**kwargs):
                all_objects.extend(page.get("Contents", []))
                all_prefixes.extend(page.get("CommonPrefixes", []))

        except Exception as e:
            handle_error(e, "list objects")

        if format == "json":
            print_json({
                "Prefixes": [p["Prefix"] for p in all_prefixes],
                "Objects": [
                    {"Key": o["Key"], "Size": o["Size"],
                     "LastModified": o["LastModified"],
                     "StorageClass": o["StorageClass"]}
                    for o in all_objects
                ],
            })
            return

        table = Table(
            title=f"Objects in {bucket}/{prefix} ({len(all_objects)} 个对象)",
            show_lines=False,
        )
        table.add_column("Key", style="cyan", min_width=30)
        table.add_column("大小", min_width=10, justify="right")
        table.add_column("最后修改", min_width=20)
        table.add_column("存储类型", min_width=10)

        # 先显示公共前缀（"目录"）
        for p in all_prefixes:
            table.add_row(f"[bold]{p['Prefix']}[/bold]", "[dim]DIR[/dim]", "-", "-")

        for obj in all_objects:
            table.add_row(
                obj["Key"],
                format_size(obj["Size"]),
                format_datetime(obj.get("LastModified")),
                obj.get("StorageClass", "-"),
            )

        console.print(table)

    # ── 上传 ─────────────────────────────────────────────────────

    def put(
        self,
        local_path: str,
        bucket: str,
        key: Optional[str] = None,
        storage_class: str = "STANDARD",
        content_type: Optional[str] = None,
    ) -> None:
        """上传本地文件到 S3

        Args:
            local_path:    本地文件路径
            bucket:        Bucket 名称
            key:           目标 Key（默认使用文件名）
            storage_class: 存储类型（默认 STANDARD）
            content_type:  Content-Type（默认自动检测）

        示例:
            s3c object put ./report.pdf my-bucket
            s3c object put ./report.pdf my-bucket --key docs/report.pdf
        """
        path = Path(local_path)
        if not path.exists():
            err_console.print(f"文件不存在: {local_path}")
            raise SystemExit(1)

        target_key = key or path.name
        file_size = path.stat().st_size

        try:
            s3 = get_s3_client()
            extra_args: dict = {"StorageClass": storage_class}
            if content_type:
                extra_args["ContentType"] = content_type

            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                FileSizeColumn(),
                TransferSpeedColumn(),
                console=console,
            ) as progress:
                task = progress.add_task(
                    f"上传 {path.name} → {bucket}/{target_key}", total=file_size
                )

                def callback(bytes_transferred: int) -> None:
                    progress.update(task, advance=bytes_transferred)

                s3.upload_file(
                    str(path), bucket, target_key,
                    ExtraArgs=extra_args,
                    Callback=callback,
                )

            console.print(f"[green]✓[/green] 上传完成: [cyan]{bucket}/{target_key}[/cyan]")
        except Exception as e:
            handle_error(e, "put object")

    # ── 下载 ─────────────────────────────────────────────────────

    def get(
        self,
        bucket: str,
        key: str,
        output: Optional[str] = None,
    ) -> None:
        """从 S3 下载文件

        Args:
            bucket: Bucket 名称
            key:    对象 Key
            output: 本地保存路径（默认使用 Key 的文件名）

        示例:
            s3c object get my-bucket docs/report.pdf
            s3c object get my-bucket docs/report.pdf --output /tmp/report.pdf
        """
        local_path = output or Path(key).name

        try:
            s3 = get_s3_client()
            # 先获取对象大小
            head = s3.head_object(Bucket=bucket, Key=key)
            file_size = head.get("ContentLength", 0)

            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                FileSizeColumn(),
                TransferSpeedColumn(),
                console=console,
            ) as progress:
                task = progress.add_task(
                    f"下载 {bucket}/{key} → {local_path}", total=file_size
                )

                def callback(bytes_transferred: int) -> None:
                    progress.update(task, advance=bytes_transferred)

                s3.download_file(
                    bucket, key, local_path, Callback=callback
                )

            console.print(f"[green]✓[/green] 下载完成: [cyan]{local_path}[/cyan]")
        except Exception as e:
            handle_error(e, "get object")

    # ── 删除 ─────────────────────────────────────────────────────

    def delete(
        self,
        bucket: str,
        key: str,
        version_id: Optional[str] = None,
    ) -> None:
        """删除 S3 对象

        Args:
            bucket:     Bucket 名称
            key:        对象 Key
            version_id: 版本 ID（可选，未指定则删除最新版本）

        示例:
            s3c object delete my-bucket docs/report.pdf
            s3c object delete my-bucket docs/report.pdf --version-id abc123
        """
        try:
            s3 = get_s3_client()
            kwargs: dict = {"Bucket": bucket, "Key": key}
            if version_id:
                kwargs["VersionId"] = version_id
            s3.delete_object(**kwargs)
            console.print(f"[green]✓[/green] 已删除: [cyan]{bucket}/{key}[/cyan]")
        except Exception as e:
            handle_error(e, "delete object")

    def delete_many(self, bucket: str, prefix: str, dry_run: bool = False) -> None:
        """批量删除指定前缀下的所有对象

        Args:
            bucket:  Bucket 名称
            prefix:  前缀（如 folder/）
            dry_run: 仅预览，不实际删除（默认 False）

        示例:
            s3c object delete-many my-bucket folder/
            s3c object delete-many my-bucket folder/ --dry-run
        """
        try:
            s3 = get_s3_client()
            paginator = s3.get_paginator("list_objects_v2")
            keys = []
            for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
                for obj in page.get("Contents", []):
                    keys.append(obj["Key"])

            if not keys:
                console.print(f"[yellow]未找到前缀 '{prefix}' 下的对象[/yellow]")
                return

            console.print(f"找到 [bold]{len(keys)}[/bold] 个对象")
            if dry_run:
                for k in keys:
                    console.print(f"  [dim](dry-run)[/dim] {k}")
                return

            # 分批删除，每批最多 1000 个
            for i in range(0, len(keys), 1000):
                batch = [{"Key": k} for k in keys[i:i+1000]]
                s3.delete_objects(Bucket=bucket, Delete={"Objects": batch})

            console.print(f"[green]✓[/green] 已删除 [bold]{len(keys)}[/bold] 个对象")
        except Exception as e:
            handle_error(e, "delete many objects")

    # ── 复制与移动 ────────────────────────────────────────────────

    def copy(
        self,
        src_bucket: str,
        src_key: str,
        dst_bucket: str,
        dst_key: str,
    ) -> None:
        """复制 S3 对象

        Args:
            src_bucket: 源 Bucket
            src_key:    源 Key
            dst_bucket: 目标 Bucket
            dst_key:    目标 Key

        示例:
            s3c object copy src-bucket old/file.txt dst-bucket new/file.txt
        """
        try:
            s3 = get_s3_client()
            s3.copy_object(
                CopySource={"Bucket": src_bucket, "Key": src_key},
                Bucket=dst_bucket,
                Key=dst_key,
            )
            console.print(
                f"[green]✓[/green] 复制完成: "
                f"[cyan]{src_bucket}/{src_key}[/cyan] → "
                f"[cyan]{dst_bucket}/{dst_key}[/cyan]"
            )
        except Exception as e:
            handle_error(e, "copy object")

    def move(
        self,
        src_bucket: str,
        src_key: str,
        dst_bucket: str,
        dst_key: str,
    ) -> None:
        """移动（复制后删除源）S3 对象

        Args:
            src_bucket: 源 Bucket
            src_key:    源 Key
            dst_bucket: 目标 Bucket
            dst_key:    目标 Key

        示例:
            s3c object move src-bucket old/file.txt dst-bucket new/file.txt
        """
        try:
            s3 = get_s3_client()
            s3.copy_object(
                CopySource={"Bucket": src_bucket, "Key": src_key},
                Bucket=dst_bucket,
                Key=dst_key,
            )
            s3.delete_object(Bucket=src_bucket, Key=src_key)
            console.print(
                f"[green]✓[/green] 移动完成: "
                f"[cyan]{src_bucket}/{src_key}[/cyan] → "
                f"[cyan]{dst_bucket}/{dst_key}[/cyan]"
            )
        except Exception as e:
            handle_error(e, "move object")

    # ── 元数据 ────────────────────────────────────────────────────

    def head(self, bucket: str, key: str, version_id: Optional[str] = None) -> None:
        """查看对象元数据（不下载内容）

        Args:
            bucket:     Bucket 名称
            key:        对象 Key
            version_id: 版本 ID（可选）

        示例:
            s3c object head my-bucket docs/report.pdf
        """
        try:
            s3 = get_s3_client()
            kwargs: dict = {"Bucket": bucket, "Key": key}
            if version_id:
                kwargs["VersionId"] = version_id
            response = s3.head_object(**kwargs)
            response.pop("ResponseMetadata", None)
            print_json(response)
        except Exception as e:
            handle_error(e, "head object")

    # ── 预签名 URL ────────────────────────────────────────────────

    def presign(
        self,
        bucket: str,
        key: str,
        expires: int = 3600,
        method: str = "get",
    ) -> None:
        """生成对象的预签名 URL

        Args:
            bucket:  Bucket 名称
            key:     对象 Key
            expires: 过期时间（秒，默认 3600）
            method:  操作类型，get 或 put（默认 get）

        示例:
            s3c object presign my-bucket docs/report.pdf
            s3c object presign my-bucket docs/report.pdf --expires 86400
            s3c object presign my-bucket upload.txt --method put
        """
        try:
            s3 = get_s3_client()
            operation_map = {
                "get": "get_object",
                "put": "put_object",
            }
            op = operation_map.get(method.lower())
            if not op:
                err_console.print(f"无效的 method: {method}，可选: get, put")
                raise SystemExit(1)

            url = s3.generate_presigned_url(
                op,
                Params={"Bucket": bucket, "Key": key},
                ExpiresIn=expires,
            )
            console.print(url)
        except SystemExit:
            raise
        except Exception as e:
            handle_error(e, "presign")

    # ── 版本管理 ──────────────────────────────────────────────────

    def versions(
        self,
        bucket: str,
        key: str,
        format: str = "table",
    ) -> None:
        """列出对象的所有版本

        Args:
            bucket: Bucket 名称
            key:    对象 Key
            format: 输出格式，table 或 json（默认 table）

        示例:
            s3c object versions my-bucket docs/report.pdf
        """
        try:
            s3 = get_s3_client()
            response = s3.list_object_versions(Bucket=bucket, Prefix=key)
            versions = [v for v in response.get("Versions", []) if v["Key"] == key]
            markers = [m for m in response.get("DeleteMarkers", []) if m["Key"] == key]
        except Exception as e:
            handle_error(e, "list versions")

        if format == "json":
            print_json({"Versions": versions, "DeleteMarkers": markers})
            return

        table = Table(title=f"对象版本: {bucket}/{key}", show_lines=False)
        table.add_column("版本 ID", style="cyan", min_width=20)
        table.add_column("类型", min_width=8)
        table.add_column("大小", min_width=10, justify="right")
        table.add_column("最后修改", min_width=20)
        table.add_column("最新", min_width=4, justify="center")

        for v in versions:
            table.add_row(
                v["VersionId"],
                "版本",
                format_size(v.get("Size", 0)),
                format_datetime(v.get("LastModified")),
                "★" if v.get("IsLatest") else "",
            )
        for m in markers:
            table.add_row(
                m["VersionId"],
                "[red]删除标记[/red]",
                "-",
                format_datetime(m.get("LastModified")),
                "★" if m.get("IsLatest") else "",
            )

        console.print(table)

    # ── 归档恢复 ──────────────────────────────────────────────────

    def restore(
        self,
        bucket: str,
        key: str,
        days: int = 7,
        tier: str = "Standard",
    ) -> None:
        """恢复归档存储类型（Glacier）的对象

        Args:
            bucket: Bucket 名称
            key:    对象 Key
            days:   临时恢复天数（默认 7）
            tier:   恢复速度，Expedited | Standard | Bulk（默认 Standard）

        示例:
            s3c object restore my-bucket archive/old.zip
            s3c object restore my-bucket archive/old.zip --days 14 --tier Expedited
        """
        try:
            s3 = get_s3_client()
            s3.restore_object(
                Bucket=bucket,
                Key=key,
                RestoreRequest={
                    "Days": days,
                    "GlacierJobParameters": {"Tier": tier},
                },
            )
            console.print(
                f"[green]✓[/green] 归档恢复请求已提交: [cyan]{bucket}/{key}[/cyan]"
                f"（{tier} 模式，{days} 天）"
            )
        except Exception as e:
            handle_error(e, "restore object")

    # ── 对象标签 ──────────────────────────────────────────────────

    def tagging_get(self, bucket: str, key: str, version_id: Optional[str] = None) -> None:
        """查看对象标签

        Args:
            bucket:     Bucket 名称
            key:        对象 Key
            version_id: 版本 ID（可选）

        示例:
            s3c object tagging-get my-bucket docs/report.pdf
        """
        try:
            s3 = get_s3_client()
            kwargs: dict = {"Bucket": bucket, "Key": key}
            if version_id:
                kwargs["VersionId"] = version_id
            response = s3.get_object_tagging(**kwargs)
            tags = {t["Key"]: t["Value"] for t in response.get("TagSet", [])}
            print_json(tags)
        except Exception as e:
            handle_error(e, "get object tagging")

    def tagging_put(self, bucket: str, key: str, tags: str) -> None:
        """设置对象标签

        Args:
            bucket: Bucket 名称
            key:    对象 Key
            tags:   标签，格式: Key1=Value1,Key2=Value2

        示例:
            s3c object tagging-put my-bucket docs/report.pdf "env=prod,owner=alice"
        """
        try:
            tag_set = [
                {"Key": k, "Value": v}
                for item in tags.split(",")
                for k, v in [item.strip().split("=", 1)]
            ]
            s3 = get_s3_client()
            s3.put_object_tagging(
                Bucket=bucket,
                Key=key,
                Tagging={"TagSet": tag_set},
            )
            console.print(f"[green]✓[/green] [cyan]{bucket}/{key}[/cyan] 标签已更新")
        except Exception as e:
            handle_error(e, "put object tagging")

    def tagging_delete(self, bucket: str, key: str) -> None:
        """删除对象所有标签

        Args:
            bucket: Bucket 名称
            key:    对象 Key

        示例:
            s3c object tagging-delete my-bucket docs/report.pdf
        """
        try:
            s3 = get_s3_client()
            s3.delete_object_tagging(Bucket=bucket, Key=key)
            console.print(f"[green]✓[/green] [cyan]{bucket}/{key}[/cyan] 标签已删除")
        except Exception as e:
            handle_error(e, "delete object tagging")

    # ── 对象 ACL ──────────────────────────────────────────────────

    def acl_get(self, bucket: str, key: str) -> None:
        """查看对象 ACL

        Args:
            bucket: Bucket 名称
            key:    对象 Key

        示例:
            s3c object acl-get my-bucket docs/report.pdf
        """
        try:
            s3 = get_s3_client()
            response = s3.get_object_acl(Bucket=bucket, Key=key)
            response.pop("ResponseMetadata", None)
            print_json(response)
        except Exception as e:
            handle_error(e, "get object acl")

    def acl_put(self, bucket: str, key: str, acl: str) -> None:
        """设置对象 ACL

        Args:
            bucket: Bucket 名称
            key:    对象 Key
            acl:    ACL 类型: private | public-read | public-read-write |
                    authenticated-read | bucket-owner-read | bucket-owner-full-control

        示例:
            s3c object acl-put my-bucket docs/report.pdf public-read
        """
        valid = {
            "private", "public-read", "public-read-write",
            "authenticated-read", "bucket-owner-read", "bucket-owner-full-control",
        }
        if acl not in valid:
            err_console.print(f"无效的 ACL 类型: {acl}\n可选: {', '.join(sorted(valid))}")
            raise SystemExit(1)
        try:
            s3 = get_s3_client()
            s3.put_object_acl(Bucket=bucket, Key=key, ACL=acl)
            console.print(f"[green]✓[/green] [cyan]{bucket}/{key}[/cyan] ACL 已设置为 {acl}")
        except Exception as e:
            handle_error(e, "put object acl")

    # ── 多段上传（Multipart） ─────────────────────────────────────

    def multipart_list(self, bucket: str, prefix: str = "") -> None:
        """列出进行中的分片上传任务

        Args:
            bucket: Bucket 名称
            prefix: 前缀过滤（可选）

        示例:
            s3c object multipart-list my-bucket
        """
        try:
            s3 = get_s3_client()
            kwargs: dict = {"Bucket": bucket}
            if prefix:
                kwargs["Prefix"] = prefix
            response = s3.list_multipart_uploads(**kwargs)
            uploads = response.get("Uploads", [])
        except Exception as e:
            handle_error(e, "list multipart uploads")

        if not uploads:
            console.print("[yellow]无进行中的分片上传任务[/yellow]")
            return

        table = Table(title=f"进行中的分片上传 ({len(uploads)})", show_lines=False)
        table.add_column("Key", style="cyan", min_width=25)
        table.add_column("Upload ID", min_width=20)
        table.add_column("发起时间", min_width=20)

        for u in uploads:
            table.add_row(
                u["Key"],
                u["UploadId"],
                format_datetime(u.get("Initiated")),
            )

        console.print(table)

    def multipart_abort(self, bucket: str, key: str, upload_id: str) -> None:
        """中止分片上传任务

        Args:
            bucket:    Bucket 名称
            key:       对象 Key
            upload_id: 分片上传 ID

        示例:
            s3c object multipart-abort my-bucket docs/large.zip abc123uploadid
        """
        try:
            s3 = get_s3_client()
            s3.abort_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id)
            console.print(f"[green]✓[/green] 分片上传已中止: [cyan]{bucket}/{key}[/cyan] ({upload_id})")
        except Exception as e:
            handle_error(e, "abort multipart upload")
