"""
Archive Manager Service for Claude MPM Documentation Versioning
==============================================================

This service manages archival and versioning of project documentation,
particularly CLAUDE.md and related files during updates.

Key Features:
- Automatic backup before updates
- Timestamped archive files
- Version comparison and diff generation
- Archive cleanup and rotation
- Restoration capabilities

Author: Claude MPM Development Team
Created: 2025-01-26
"""

import difflib
import gzip
import hashlib
import json
import re
import shutil
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Optional, Tuple

from rich.console import Console
from rich.table import Table

from claude_mpm.core.logging_utils import get_logger

logger = get_logger(__name__)
console = Console()


class ArchiveManager:
    """Manages documentation archival and versioning."""

    # Archive settings
    ARCHIVE_DIR = "docs/_archive"
    MAX_ARCHIVES = 10  # Maximum number of archives to keep per file
    COMPRESS_AFTER_DAYS = 7  # Compress archives older than this
    DELETE_AFTER_DAYS = 90  # Delete archives older than this

    def __init__(self, project_path: Path):
        """Initialize the archive manager."""
        self.project_path = project_path
        self.archive_path = project_path / self.ARCHIVE_DIR
        self.is_git_repo = (project_path / ".git").exists()
        self._ensure_archive_directory()

        # Documentation patterns
        self.version_patterns = {
            "semantic": re.compile(r"v?(\d+)\.(\d+)\.(\d+)"),
            "date": re.compile(r"(\d{4})-(\d{2})-(\d{2})"),
            "build": re.compile(r"build[.\-](\d+)"),
        }

        # Key documentation files to track
        self.key_docs = {
            "CLAUDE.md": "Project instructions and guidelines",
            "README.md": "Project overview and setup",
            "CHANGELOG.md": "Version history and changes",
            "docs/README.md": "Documentation index",
        }

    def _ensure_archive_directory(self) -> None:
        """Ensure archive directory exists."""
        self.archive_path.mkdir(parents=True, exist_ok=True)

        # Create README if not exists
        readme_path = self.archive_path / "README.md"
        if not readme_path.exists():
            readme_content = """# Documentation Archives

This directory contains archived versions of project documentation files.

## Archive Naming Convention

Files are archived with timestamps:
- `CLAUDE.md.2024-01-15T10-30-45.md` - Regular archive
- `CLAUDE.md.2024-01-15T10-30-45.md.gz` - Compressed archive (older than 7 days)

## Archive Management

- Archives are created automatically when documentation is updated
- Older archives are compressed after 7 days
- Archives older than 90 days are automatically deleted
- Maximum of 10 archives kept per file

## Restoration

To restore an archived file:
1. Find the desired version by timestamp
2. Copy it back to the project root
3. Rename to remove the timestamp

Generated by Claude MPM Archive Manager
"""
            readme_path.write_text(readme_content)

    def archive_file(
        self,
        file_path: Path,
        reason: Optional[str] = None,
        metadata: Optional[Dict] = None,
    ) -> Optional[Path]:
        """Archive a file with timestamp and optional metadata."""
        if not file_path.exists():
            logger.warning(f"File {file_path} does not exist, cannot archive")
            return None

        try:
            # Generate archive filename
            timestamp = datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
            archive_name = f"{file_path.name}.{timestamp}{file_path.suffix}"
            archive_file_path = self.archive_path / archive_name

            # Copy file to archive
            shutil.copy2(file_path, archive_file_path)
            logger.info(f"Archived {file_path.name} to {archive_file_path}")

            # Create metadata file if provided
            if metadata or reason:
                meta_data = metadata or {}
                meta_data.update(
                    {
                        "original_path": str(file_path),
                        "archived_at": datetime.now().isoformat(),
                        "reason": reason or "Manual archive",
                        "file_size": file_path.stat().st_size,
                        "file_hash": self._calculate_file_hash(file_path),
                    }
                )

                meta_path = self.archive_path / f"{archive_name}.meta.json"
                meta_path.write_text(json.dumps(meta_data, indent=2))

            # Cleanup old archives
            self._cleanup_archives(file_path.name)

            return archive_file_path

        except Exception as e:
            logger.error(f"Failed to archive {file_path}: {e}")
            return None

    def _calculate_file_hash(self, file_path: Path) -> str:
        """Calculate MD5 hash of file."""
        hasher = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hasher.update(chunk)
        return hasher.hexdigest()

    def _cleanup_archives(self, original_filename: str) -> None:
        """Clean up old archives for a specific file."""
        # Find all archives for this file
        archives = self._find_archives(original_filename)

        # Sort by modification time
        archives.sort(key=lambda p: p.stat().st_mtime)

        # Remove oldest if exceeding max count
        if len(archives) > self.MAX_ARCHIVES:
            for archive in archives[: -self.MAX_ARCHIVES]:
                self._remove_archive(archive)
                logger.info(f"Removed old archive: {archive.name}")

        # Compress old archives
        cutoff_compress = datetime.now() - timedelta(days=self.COMPRESS_AFTER_DAYS)
        for archive in archives:
            if not archive.suffix == ".gz":
                mtime = datetime.fromtimestamp(archive.stat().st_mtime)
                if mtime < cutoff_compress:
                    self._compress_archive(archive)

        # Delete very old archives
        cutoff_delete = datetime.now() - timedelta(days=self.DELETE_AFTER_DAYS)
        for archive in archives:
            mtime = datetime.fromtimestamp(archive.stat().st_mtime)
            if mtime < cutoff_delete:
                self._remove_archive(archive)
                logger.info(f"Deleted old archive: {archive.name}")

    def _find_archives(self, original_filename: str) -> List[Path]:
        """Find all archives for a specific file."""
        archives = []
        pattern = f"{original_filename}.*"

        for file in self.archive_path.glob(pattern):
            # Skip metadata files
            if not file.name.endswith(".meta.json"):
                archives.append(file)

        return archives

    def _compress_archive(self, archive_path: Path) -> None:
        """Compress an archive file using gzip."""
        try:
            compressed_path = archive_path.with_suffix(archive_path.suffix + ".gz")

            with open(archive_path, "rb") as f_in:
                with gzip.open(compressed_path, "wb") as f_out:
                    shutil.copyfileobj(f_in, f_out)

            # Remove original after successful compression
            archive_path.unlink()
            logger.debug(f"Compressed archive: {archive_path.name}")

            # Update metadata file if exists
            meta_path = self.archive_path / f"{archive_path.name}.meta.json"
            if meta_path.exists():
                new_meta_path = self.archive_path / f"{compressed_path.name}.meta.json"
                meta_path.rename(new_meta_path)

        except Exception as e:
            logger.error(f"Failed to compress {archive_path}: {e}")

    def _remove_archive(self, archive_path: Path) -> None:
        """Remove an archive and its metadata."""
        try:
            # Remove main file
            if archive_path.exists():
                archive_path.unlink()

            # Remove metadata if exists
            meta_path = self.archive_path / f"{archive_path.name}.meta.json"
            if meta_path.exists():
                meta_path.unlink()

        except Exception as e:
            logger.error(f"Failed to remove archive {archive_path}: {e}")

    def list_archives(
        self, filename: Optional[str] = None, include_metadata: bool = False
    ) -> List[Dict]:
        """List all archives or archives for a specific file."""
        archives = []

        if filename:
            archive_files = self._find_archives(filename)
        else:
            archive_files = [
                f
                for f in self.archive_path.glob("*")
                if not f.name.endswith(".meta.json") and not f.name == "README.md"
            ]

        for archive_file in archive_files:
            info = {
                "name": archive_file.name,
                "path": str(archive_file),
                "size": archive_file.stat().st_size,
                "modified": datetime.fromtimestamp(
                    archive_file.stat().st_mtime
                ).isoformat(),
                "compressed": archive_file.suffix == ".gz",
            }

            # Add metadata if requested and available
            if include_metadata:
                meta_path = self.archive_path / f"{archive_file.name}.meta.json"
                if meta_path.exists():
                    try:
                        info["metadata"] = json.loads(meta_path.read_text())
                    except Exception:
                        pass

            archives.append(info)

        # Sort by modification time, newest first
        archives.sort(key=lambda x: x["modified"], reverse=True)

        return archives

    def get_latest_archive(self, filename: str) -> Optional[Path]:
        """Get the most recent archive for a file."""
        archives = self._find_archives(filename)
        if not archives:
            return None

        # Sort by modification time and return latest
        archives.sort(key=lambda p: p.stat().st_mtime, reverse=True)
        return archives[0]

    def restore_archive(
        self, archive_name: str, target_path: Optional[Path] = None
    ) -> Tuple[bool, str]:
        """Restore an archived file to its original location or specified path."""
        archive_file = self.archive_path / archive_name

        if not archive_file.exists():
            return False, f"Archive {archive_name} not found"

        try:
            # Determine target path
            if target_path is None:
                # Extract original filename from archive name
                # Format: original.ext.timestamp.ext[.gz]
                parts = archive_name.split(".")
                if archive_name.endswith(".gz"):
                    # Remove .gz and timestamp
                    original_name = ".".join(parts[:-3])
                else:
                    # Remove timestamp
                    original_name = ".".join(parts[:-2])
                target_path = self.project_path / original_name

            # Backup current file if it exists
            if target_path.exists():
                self.archive_file(
                    target_path,
                    reason="Backup before restoration",
                    metadata={"restoration_from": archive_name},
                )

            # Restore file
            if archive_file.suffix == ".gz":
                # Decompress first
                with gzip.open(archive_file, "rb") as f_in:
                    with open(target_path, "wb") as f_out:
                        shutil.copyfileobj(f_in, f_out)
            else:
                shutil.copy2(archive_file, target_path)

            logger.info(f"Restored {archive_name} to {target_path}")
            return True, f"Successfully restored to {target_path}"

        except Exception as e:
            logger.error(f"Failed to restore {archive_name}: {e}")
            return False, f"Restoration failed: {e!s}"

    def compare_with_archive(self, current_file: Path, archive_name: str) -> Dict:
        """Compare current file with an archived version."""
        archive_file = self.archive_path / archive_name

        if not archive_file.exists():
            return {"error": f"Archive {archive_name} not found"}

        if not current_file.exists():
            return {"error": f"Current file {current_file} not found"}

        try:
            # Read archived content
            if archive_file.suffix == ".gz":
                with gzip.open(archive_file, "rt", encoding="utf-8") as f:
                    archive_content = f.read()
            else:
                archive_content = archive_file.read_text()

            # Read current content
            current_content = current_file.read_text()

            # Calculate differences
            current_lines = current_content.splitlines()
            archive_lines = archive_content.splitlines()

            return {
                "current_file": str(current_file),
                "archive_file": archive_name,
                "current_lines": len(current_lines),
                "archive_lines": len(archive_lines),
                "lines_added": len(current_lines) - len(archive_lines),
                "current_size": len(current_content),
                "archive_size": len(archive_content),
                "size_change": len(current_content) - len(archive_content),
                "current_hash": self._calculate_file_hash(current_file),
                "identical": current_content == archive_content,
            }

        except Exception as e:
            return {"error": f"Comparison failed: {e!s}"}

    def create_archive_report(self) -> Dict:
        """Generate a report of all archives."""
        report = {
            "archive_directory": str(self.archive_path),
            "total_archives": 0,
            "total_size": 0,
            "compressed_count": 0,
            "files_tracked": {},
            "oldest_archive": None,
            "newest_archive": None,
        }

        archives = self.list_archives(include_metadata=True)
        report["total_archives"] = len(archives)

        for archive in archives:
            report["total_size"] += archive["size"]
            if archive["compressed"]:
                report["compressed_count"] += 1

            # Track by original file
            original = archive["name"].split(".")[0]
            if original not in report["files_tracked"]:
                report["files_tracked"][original] = {
                    "count": 0,
                    "total_size": 0,
                    "versions": [],
                }

            report["files_tracked"][original]["count"] += 1
            report["files_tracked"][original]["total_size"] += archive["size"]
            report["files_tracked"][original]["versions"].append(archive["name"])

        # Find oldest and newest
        if archives:
            report["oldest_archive"] = archives[-1]["name"]
            report["newest_archive"] = archives[0]["name"]

        return report

    def auto_archive_before_update(
        self, file_path: Path, update_reason: str = "Before update"
    ) -> bool:
        """Automatically archive a file before updating it."""
        if not file_path.exists():
            logger.debug(f"File {file_path} does not exist, skipping archive")
            return True

        # Check if content has changed since last archive
        latest = self.get_latest_archive(file_path.name)
        if latest:
            comparison = self.compare_with_archive(file_path, latest.name)
            if comparison.get("identical"):
                logger.debug(f"File {file_path.name} unchanged, skipping archive")
                return True

        # Archive the file
        result = self.archive_file(
            file_path,
            reason=update_reason,
            metadata={
                "update_type": "mpm-init",
                "auto_archive": True,
            },
        )

        return result is not None

    # ========== Git Integration Methods ==========

    def _run_git_command(self, args: List[str]) -> Optional[str]:
        """Run a git command and return output."""
        if not self.is_git_repo:
            return None

        try:
            result = subprocess.run(
                ["git"] + args,
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )
            return result.stdout.strip()
        except subprocess.CalledProcessError as e:
            logger.debug(f"Git command failed: {e}")
            return None

    def get_file_git_history(self, file_path: Path, limit: int = 10) -> List[Dict]:
        """Get git commit history for a specific file."""
        if not self.is_git_repo or not file_path.exists():
            return []

        relative_path = file_path.relative_to(self.project_path)
        output = self._run_git_command(
            [
                "log",
                f"-{limit}",
                "--pretty=format:%H|%an|%at|%s",
                "--follow",
                str(relative_path),
            ]
        )

        if not output:
            return []

        commits = []
        for line in output.splitlines():
            parts = line.split("|", 3)
            if len(parts) == 4:
                commits.append(
                    {
                        "hash": parts[0][:8],
                        "author": parts[1],
                        "date": datetime.fromtimestamp(int(parts[2])).isoformat(),
                        "message": parts[3],
                    }
                )
        return commits

    def get_file_last_modified(self, file_path: Path) -> Optional[datetime]:
        """Get the last git modification date for a file."""
        if not self.is_git_repo or not file_path.exists():
            return None

        relative_path = file_path.relative_to(self.project_path)
        output = self._run_git_command(
            [
                "log",
                "-1",
                "--format=%at",
                str(relative_path),
            ]
        )

        if output:
            return datetime.fromtimestamp(int(output))
        return None

    # ========== Documentation Review Methods ==========

    def review_documentation(self, check_git: bool = True) -> Dict:
        """Comprehensive documentation review with outdated detection."""
        report = {
            "timestamp": datetime.now().isoformat(),
            "files_reviewed": {},
            "outdated_sections": [],
            "synchronization_issues": [],
            "recommendations": [],
        }

        # Review each key documentation file
        for doc_file, description in self.key_docs.items():
            file_path = self.project_path / doc_file
            if file_path.exists():
                file_report = self._review_single_doc(file_path, check_git)
                report["files_reviewed"][doc_file] = file_report

                # Check for outdated content
                if file_report.get("outdated_indicators"):
                    report["outdated_sections"].append(
                        {
                            "file": doc_file,
                            "indicators": file_report["outdated_indicators"],
                        }
                    )

        # Check synchronization between docs
        sync_issues = self._check_documentation_sync()
        if sync_issues:
            report["synchronization_issues"] = sync_issues

        # Generate recommendations
        report["recommendations"] = self._generate_recommendations(report)

        return report

    def _review_single_doc(self, file_path: Path, check_git: bool) -> Dict:
        """Review a single documentation file for outdated content."""
        content = file_path.read_text()
        report = {
            "exists": True,
            "size": len(content),
            "lines": len(content.splitlines()),
            "last_modified": file_path.stat().st_mtime,
            "outdated_indicators": [],
            "version_references": [],
        }

        # Check git history if available
        if check_git and self.is_git_repo:
            git_history = self.get_file_git_history(file_path, limit=5)
            if git_history:
                report["last_git_update"] = git_history[0]["date"]
                report["recent_changes"] = len(git_history)

        # Find version references
        for pattern_name, pattern in self.version_patterns.items():
            matches = pattern.findall(content)
            if matches:
                report["version_references"].append(
                    {
                        "type": pattern_name,
                        "versions": matches[:5],  # First 5 matches
                    }
                )

        # Detect outdated indicators
        outdated_indicators = self._detect_outdated_content(content, file_path.name)
        if outdated_indicators:
            report["outdated_indicators"] = outdated_indicators

        return report

    def _detect_outdated_content(self, content: str, filename: str) -> List[Dict]:
        """Detect potentially outdated content in documentation."""
        indicators = []
        lines = content.splitlines()

        # Pattern-based outdated detection
        outdated_patterns = [
            (r"TODO|FIXME|XXX", "Unresolved TODOs"),
            (r"deprecated|obsolete|legacy", "Deprecated references"),
            (r"coming soon|upcoming|future release", "Future tense content"),
            (r"alpha|beta|experimental", "Pre-release indicators"),
            (r"temporary|workaround|hack", "Temporary solutions"),
        ]

        for pattern, description in outdated_patterns:
            regex = re.compile(pattern, re.IGNORECASE)
            for i, line in enumerate(lines, 1):
                if regex.search(line):
                    indicators.append(
                        {
                            "line": i,
                            "type": description,
                            "content": line.strip()[:100],  # First 100 chars
                        }
                    )

        # Check for old version numbers if VERSION file exists
        version_file = self.project_path / "VERSION"
        if version_file.exists():
            current_version = version_file.read_text().strip()
            old_version_pattern = re.compile(r"v?(\d+)\.(\d+)\.(\d+)")

            for match in old_version_pattern.finditer(content):
                found_version = match.group(0)
                if found_version != current_version and self._is_older_version(
                    found_version, current_version
                ):
                    pos = content[: match.start()].count("\n") + 1
                    indicators.append(
                        {
                            "line": pos,
                            "type": "Old version reference",
                            "content": f"Found {found_version} (current: {current_version})",
                        }
                    )

        return indicators[:20]  # Limit to 20 most relevant

    def _is_older_version(self, version1: str, version2: str) -> bool:
        """Compare two version strings."""
        try:
            # Parse semantic versions
            v1_match = self.version_patterns["semantic"].search(version1)
            v2_match = self.version_patterns["semantic"].search(version2)

            if v1_match and v2_match:
                v1 = tuple(map(int, v1_match.groups()))
                v2 = tuple(map(int, v2_match.groups()))
                return v1 < v2
        except Exception:
            pass
        return False

    def _check_documentation_sync(self) -> List[Dict]:
        """Check synchronization between key documentation files."""
        issues = []

        # Check CLAUDE.md vs README.md
        claude_path = self.project_path / "CLAUDE.md"
        readme_path = self.project_path / "README.md"

        if claude_path.exists() and readme_path.exists():
            claude_content = claude_path.read_text()
            readme_content = readme_path.read_text()

            # Check for version discrepancies
            claude_versions = self.version_patterns["semantic"].findall(claude_content)
            readme_versions = self.version_patterns["semantic"].findall(readme_content)

            if claude_versions and readme_versions:
                if claude_versions[0] != readme_versions[0]:
                    issues.append(
                        {
                            "type": "Version mismatch",
                            "files": ["CLAUDE.md", "README.md"],
                            "details": f"CLAUDE.md: {claude_versions[0]}, README.md: {readme_versions[0]}",
                        }
                    )

            # Check for project name consistency
            project_names = re.findall(
                r"Claude MPM|claude-mpm", readme_content, re.IGNORECASE
            )
            if project_names:
                unique_names = set(project_names)
                if len(unique_names) > 1:
                    issues.append(
                        {
                            "type": "Inconsistent project naming",
                            "files": ["README.md"],
                            "details": f"Found variations: {', '.join(unique_names)}",
                        }
                    )

        # Check CHANGELOG.md exists and is recent
        changelog_path = self.project_path / "CHANGELOG.md"
        if changelog_path.exists():
            last_modified = self.get_file_last_modified(changelog_path)
            if last_modified:
                days_old = (datetime.now() - last_modified).days
                if days_old > 30:
                    issues.append(
                        {
                            "type": "Stale changelog",
                            "files": ["CHANGELOG.md"],
                            "details": f"Last updated {days_old} days ago",
                        }
                    )
        else:
            issues.append(
                {
                    "type": "Missing file",
                    "files": ["CHANGELOG.md"],
                    "details": "CHANGELOG.md does not exist",
                }
            )

        return issues

    def _generate_recommendations(self, report: Dict) -> List[str]:
        """Generate actionable recommendations from review report."""
        recommendations = []

        # Check for outdated content
        if report["outdated_sections"]:
            recommendations.append(
                f"📝 Review and update {len(report['outdated_sections'])} files with outdated content"
            )

        # Check for sync issues
        if report["synchronization_issues"]:
            for issue in report["synchronization_issues"]:
                if issue["type"] == "Version mismatch":
                    recommendations.append(
                        "🔄 Synchronize version numbers across documentation files"
                    )
                elif issue["type"] == "Stale changelog":
                    recommendations.append("📅 Update CHANGELOG.md with recent changes")
                elif issue["type"] == "Missing file":
                    recommendations.append(f"➕ Create missing {issue['files'][0]}")

        # Check for TODO items
        total_todos = sum(
            len(
                [
                    i
                    for i in file_report.get("outdated_indicators", [])
                    if i["type"] == "Unresolved TODOs"
                ]
            )
            for file_report in report["files_reviewed"].values()
        )
        if total_todos > 0:
            recommendations.append(
                f"✅ Resolve {total_todos} TODO items in documentation"
            )

        # Check for deprecated references
        deprecated_count = sum(
            len(
                [
                    i
                    for i in file_report.get("outdated_indicators", [])
                    if "deprecated" in i["type"].lower()
                ]
            )
            for file_report in report["files_reviewed"].values()
        )
        if deprecated_count > 0:
            recommendations.append(f"⚠️ Update {deprecated_count} deprecated references")

        return recommendations

    def auto_detect_and_archive_outdated(self, dry_run: bool = False) -> Dict:
        """Automatically detect and archive outdated documentation."""
        result = {
            "reviewed_files": [],
            "archived_files": [],
            "skipped_files": [],
        }

        # Review documentation
        review = self.review_documentation()

        for filename, file_report in review["files_reviewed"].items():
            file_path = self.project_path / filename
            result["reviewed_files"].append(filename)

            # Determine if file should be archived
            should_archive = False
            archive_reason = []

            # Check for significant outdated content
            outdated_count = len(file_report.get("outdated_indicators", []))
            if outdated_count > 10:
                should_archive = True
                archive_reason.append(f"{outdated_count} outdated indicators")

            # Check if file hasn't been updated in git for long time
            if file_report.get("last_git_update"):
                last_update = datetime.fromisoformat(file_report["last_git_update"])
                days_old = (datetime.now() - last_update).days
                if days_old > 90:
                    should_archive = True
                    archive_reason.append(f"No updates for {days_old} days")

            # Archive if needed
            if should_archive and not dry_run:
                archive_result = self.archive_file(
                    file_path,
                    reason=f"Auto-archived: {', '.join(archive_reason)}",
                    metadata={
                        "auto_detection": True,
                        "indicators": file_report.get("outdated_indicators", [])[:5],
                        "review_timestamp": review["timestamp"],
                    },
                )
                if archive_result:
                    result["archived_files"].append(
                        {
                            "file": filename,
                            "reason": archive_reason,
                            "archive_path": str(archive_result),
                        }
                    )
            elif should_archive:
                result["skipped_files"].append(
                    {
                        "file": filename,
                        "reason": archive_reason,
                        "action": "Would archive (dry run)",
                    }
                )

        return result

    def generate_documentation_diff_report(
        self, file1: Path, file2: Optional[Path] = None
    ) -> str:
        """Generate a diff report between two documentation files or versions."""
        if not file1.exists():
            return f"Error: {file1} does not exist"

        # If file2 not specified, compare with latest archive
        if file2 is None:
            latest = self.get_latest_archive(file1.name)
            if not latest:
                return f"No archive found for {file1.name}"
            file2 = latest

        # Read contents
        content1 = file1.read_text()

        if file2.suffix == ".gz":
            with gzip.open(file2, "rt", encoding="utf-8") as f:
                content2 = f.read()
        else:
            content2 = file2.read_text() if isinstance(file2, Path) else file2

        # Generate diff
        diff = difflib.unified_diff(
            content2.splitlines(keepends=True),
            content1.splitlines(keepends=True),
            fromfile=str(file2) if isinstance(file2, Path) else "Archive",
            tofile=str(file1),
            n=3,
        )

        return "".join(diff)

    def sync_with_readme_and_changelog(self) -> Dict:
        """Sync key information between CLAUDE.md, README.md, and CHANGELOG.md."""
        result = {
            "synced": False,
            "changes": [],
            "errors": [],
        }

        claude_path = self.project_path / "CLAUDE.md"
        readme_path = self.project_path / "README.md"
        changelog_path = self.project_path / "CHANGELOG.md"

        if not claude_path.exists():
            result["errors"].append("CLAUDE.md not found")
            return result

        try:
            # Extract current version
            version_file = self.project_path / "VERSION"
            if version_file.exists():
                current_version = version_file.read_text().strip()
            else:
                current_version = None

            # Update README.md if exists
            if readme_path.exists() and current_version:
                readme_content = readme_path.read_text()
                # Update version badges or references
                updated_readme = self._update_version_references(
                    readme_content, current_version
                )
                if updated_readme != readme_content:
                    # Archive before update
                    self.archive_file(readme_path, reason="Before version sync")
                    readme_path.write_text(updated_readme)
                    result["changes"].append(
                        f"Updated README.md to version {current_version}"
                    )

            # Update CHANGELOG.md header if exists
            if changelog_path.exists() and current_version:
                changelog_content = changelog_path.read_text()
                if f"## [{current_version}]" not in changelog_content:
                    # Add new version section
                    today = datetime.now().strftime("%Y-%m-%d")
                    new_section = f"\n## [{current_version}] - {today}\n\n### Added\n\n### Changed\n\n### Fixed\n\n"

                    # Insert after header
                    lines = changelog_content.splitlines()
                    insert_pos = 0
                    for i, line in enumerate(lines):
                        if line.startswith("## "):
                            insert_pos = i
                            break

                    if insert_pos > 0:
                        lines.insert(insert_pos, new_section)
                        updated_changelog = "\n".join(lines)

                        # Archive before update
                        self.archive_file(
                            changelog_path, reason="Before adding new version"
                        )
                        changelog_path.write_text(updated_changelog)
                        result["changes"].append(
                            f"Added {current_version} section to CHANGELOG.md"
                        )

            result["synced"] = len(result["changes"]) > 0

        except Exception as e:
            result["errors"].append(f"Sync failed: {e!s}")
            logger.error(f"Documentation sync failed: {e}")

        return result

    def _update_version_references(self, content: str, new_version: str) -> str:
        """Update version references in documentation content."""
        # Update version badges
        content = re.sub(
            r"badge/version-v?\d+\.\d+\.\d+",
            f"badge/version-v{new_version}",
            content,
        )

        # Update explicit version mentions
        content = re.sub(
            r"(?:Version|version|v)\s*\d+\.\d+\.\d+",
            f"v{new_version}",
            content,
        )

        return content

    def display_review_summary(self, review: Dict) -> None:
        """Display a formatted summary of the documentation review."""
        console.print("\n[bold cyan]📚 Documentation Review Summary[/bold cyan]\n")

        # Create summary table
        table = Table(title="Documentation Files Status")
        table.add_column("File", style="cyan")
        table.add_column("Status", style="green")
        table.add_column("Issues", style="yellow")
        table.add_column("Last Updated", style="magenta")

        for filename, report in review["files_reviewed"].items():
            status = (
                "✅ OK" if not report.get("outdated_indicators") else "⚠️ Needs Review"
            )
            issues = len(report.get("outdated_indicators", []))

            last_updated = "Unknown"
            if report.get("last_git_update"):
                last_date = datetime.fromisoformat(report["last_git_update"])
                days_ago = (datetime.now() - last_date).days
                last_updated = f"{days_ago} days ago"

            table.add_row(filename, status, str(issues), last_updated)

        console.print(table)

        # Print synchronization issues
        if review["synchronization_issues"]:
            console.print("\n[bold yellow]⚠️ Synchronization Issues:[/bold yellow]")
            for issue in review["synchronization_issues"]:
                console.print(f"  • {issue['type']}: {issue['details']}")

        # Print recommendations
        if review["recommendations"]:
            console.print("\n[bold green]💡 Recommendations:[/bold green]")
            for rec in review["recommendations"]:
                console.print(f"  {rec}")

    def restore_from_archive_with_review(
        self, archive_name: str, review_changes: bool = True
    ) -> Tuple[bool, str]:
        """Restore an archive with optional change review."""
        archive_file = self.archive_path / archive_name

        if not archive_file.exists():
            return False, f"Archive {archive_name} not found"

        # Extract target file name
        parts = archive_name.split(".")
        if archive_name.endswith(".gz"):
            original_name = ".".join(parts[:-3])
        else:
            original_name = ".".join(parts[:-2])

        target_path = self.project_path / original_name

        # Generate diff if current file exists and review requested
        if target_path.exists() and review_changes:
            diff_report = self.generate_documentation_diff_report(
                target_path, archive_file
            )

            console.print("\n[bold cyan]📝 Changes to be applied:[/bold cyan]")
            console.print(diff_report)

            # Ask for confirmation
            console.print(
                "\n[bold yellow]Proceed with restoration? (y/n): [/bold yellow]", end=""
            )
            # In automated context, assume yes
            confirm = True

            if not confirm:
                return False, "Restoration cancelled by user"

        # Proceed with restoration
        return self.restore_archive(archive_name, target_path)
