"""
Quick-Win Finder

Identifies easy improvement opportunities in a codebase:
- Inline styles that can be extracted
- Duplicate values that should be tokens
- Unused styles
- Naming inconsistencies
- Accessibility issues
"""

import re
from pathlib import Path
from typing import List, Dict, Any, Optional
from dataclasses import dataclass

from .base import (
    QuickWin,
    QuickWinType,
    QuickWinPriority,
    Location,
    ProjectAnalysis,
)
from .styles import StyleAnalyzer
from .react import ReactAnalyzer


class QuickWinFinder:
    """
    Finds quick improvement opportunities in a project.

    Categories:
    - INLINE_STYLE: Inline styles that can be extracted to CSS/tokens
    - DUPLICATE_VALUE: Repeated values that should be tokens
    - UNUSED_STYLE: CSS that's defined but not used
    - HARDCODED_VALUE: Magic numbers/colors that should be tokens
    - NAMING_INCONSISTENCY: Inconsistent naming patterns
    - DEPRECATED_PATTERN: Outdated styling approaches
    - ACCESSIBILITY: A11y improvements
    """

    def __init__(self, root_path: str):
        self.root = Path(root_path).resolve()
        self.style_analyzer = StyleAnalyzer(root_path)
        self.react_analyzer = ReactAnalyzer(root_path)

    async def find_all(self) -> List[QuickWin]:
        """
        Find all quick-win opportunities.

        Returns:
            List of QuickWin objects sorted by priority
        """
        quick_wins = []

        # Find inline styles
        inline_wins = await self._find_inline_style_wins()
        quick_wins.extend(inline_wins)

        # Find duplicate values
        duplicate_wins = await self._find_duplicate_value_wins()
        quick_wins.extend(duplicate_wins)

        # Find unused styles
        unused_wins = await self._find_unused_style_wins()
        quick_wins.extend(unused_wins)

        # Find hardcoded values
        hardcoded_wins = await self._find_hardcoded_value_wins()
        quick_wins.extend(hardcoded_wins)

        # Find naming inconsistencies
        naming_wins = await self._find_naming_inconsistency_wins()
        quick_wins.extend(naming_wins)

        # Find accessibility issues
        a11y_wins = await self._find_accessibility_wins()
        quick_wins.extend(a11y_wins)

        # Sort by priority
        priority_order = {
            QuickWinPriority.CRITICAL: 0,
            QuickWinPriority.HIGH: 1,
            QuickWinPriority.MEDIUM: 2,
            QuickWinPriority.LOW: 3,
        }
        quick_wins.sort(key=lambda x: priority_order[x.priority])

        return quick_wins

    async def _find_inline_style_wins(self) -> List[QuickWin]:
        """Find inline styles that should be extracted."""
        wins = []

        inline_styles = await self.react_analyzer.find_inline_styles()

        if not inline_styles:
            return wins

        # Group by file
        by_file = {}
        for style in inline_styles:
            file_path = style['file']
            if file_path not in by_file:
                by_file[file_path] = []
            by_file[file_path].append(style)

        # Create quick-wins for files with multiple inline styles
        for file_path, styles in by_file.items():
            if len(styles) >= 3:  # Only flag if 3+ inline styles
                wins.append(QuickWin(
                    type=QuickWinType.INLINE_STYLE,
                    priority=QuickWinPriority.HIGH,
                    title=f"Extract {len(styles)} inline styles",
                    description=f"File {file_path} has {len(styles)} inline style declarations that could be extracted to CSS classes or design tokens.",
                    location=Location(file_path, styles[0]['line']),
                    affected_files=[file_path],
                    estimated_impact=f"Reduce inline styles, improve maintainability",
                    fix_suggestion="Extract repeated style properties to CSS classes or design tokens. Use className instead of style prop.",
                    auto_fixable=True,
                ))

        # Create summary if many files have inline styles
        total_inline = len(inline_styles)
        if total_inline >= 10:
            wins.insert(0, QuickWin(
                type=QuickWinType.INLINE_STYLE,
                priority=QuickWinPriority.HIGH,
                title=f"Project has {total_inline} inline styles",
                description=f"Found {total_inline} inline style declarations across {len(by_file)} files. Consider migrating to CSS classes or design tokens.",
                affected_files=list(by_file.keys())[:10],
                estimated_impact=f"Improve code maintainability and bundle size",
                fix_suggestion="Run 'dss migrate inline-styles' to preview migration options.",
                auto_fixable=True,
            ))

        return wins

    async def _find_duplicate_value_wins(self) -> List[QuickWin]:
        """Find duplicate values that should be tokens."""
        wins = []

        analysis = await self.style_analyzer.analyze()
        duplicates = analysis.get('duplicates', [])

        # Find high-occurrence duplicates
        for dup in duplicates[:10]:  # Top 10 duplicates
            if dup['count'] >= 5:  # Only if used 5+ times
                priority = QuickWinPriority.HIGH if dup['count'] >= 10 else QuickWinPriority.MEDIUM

                wins.append(QuickWin(
                    type=QuickWinType.DUPLICATE_VALUE,
                    priority=priority,
                    title=f"Duplicate value '{dup['value']}' used {dup['count']} times",
                    description=f"The value '{dup['value']}' appears {dup['count']} times across {len(dup['files'])} files. This should be a design token.",
                    affected_files=dup['files'],
                    estimated_impact=f"Create single source of truth, easier theme updates",
                    fix_suggestion=f"Create token for this value and replace all occurrences.",
                    auto_fixable=True,
                ))

        return wins

    async def _find_unused_style_wins(self) -> List[QuickWin]:
        """Find unused CSS styles."""
        wins = []

        unused = await self.style_analyzer.find_unused_styles()

        if len(unused) >= 5:
            wins.append(QuickWin(
                type=QuickWinType.UNUSED_STYLE,
                priority=QuickWinPriority.MEDIUM,
                title=f"Found {len(unused)} potentially unused CSS classes",
                description=f"These CSS classes are defined but don't appear to be used in the codebase. Review and remove if confirmed unused.",
                affected_files=list(set(u['file'] for u in unused))[:10],
                estimated_impact=f"Reduce CSS bundle size by removing dead code",
                fix_suggestion="Review each class and remove if unused. Some may be dynamically generated.",
                auto_fixable=False,  # Needs human review
            ))

        return wins

    async def _find_hardcoded_value_wins(self) -> List[QuickWin]:
        """Find hardcoded magic values."""
        wins = []

        analysis = await self.style_analyzer.analyze()
        candidates = analysis.get('token_candidates', [])

        # Find high-confidence candidates
        high_confidence = [c for c in candidates if c.confidence >= 0.7]

        if high_confidence:
            wins.append(QuickWin(
                type=QuickWinType.HARDCODED_VALUE,
                priority=QuickWinPriority.MEDIUM,
                title=f"Found {len(high_confidence)} values that should be tokens",
                description="These hardcoded values appear multiple times and should be extracted as design tokens for consistency.",
                estimated_impact="Improve theme consistency and make updates easier",
                fix_suggestion="Use 'dss extract-tokens' to create tokens from these values.",
                auto_fixable=True,
            ))

            # Add specific wins for top candidates
            for candidate in high_confidence[:5]:
                wins.append(QuickWin(
                    type=QuickWinType.HARDCODED_VALUE,
                    priority=QuickWinPriority.LOW,
                    title=f"Extract '{candidate.value}' as token",
                    description=f"Value '{candidate.value}' appears {candidate.occurrences} times. Suggested token: {candidate.suggested_name}",
                    location=candidate.locations[0] if candidate.locations else None,
                    affected_files=[loc.file_path for loc in candidate.locations[:5]],
                    estimated_impact=f"Single source of truth for this value",
                    fix_suggestion=f"Create token '{candidate.suggested_name}' with value '{candidate.value}'",
                    auto_fixable=True,
                ))

        return wins

    async def _find_naming_inconsistency_wins(self) -> List[QuickWin]:
        """Find naming inconsistencies."""
        wins = []

        naming = await self.style_analyzer.analyze_naming_consistency()

        if naming.get('inconsistencies'):
            primary = naming.get('primary_pattern', 'unknown')
            inconsistent_count = len(naming['inconsistencies'])

            wins.append(QuickWin(
                type=QuickWinType.NAMING_INCONSISTENCY,
                priority=QuickWinPriority.LOW,
                title=f"Found {inconsistent_count} naming inconsistencies",
                description=f"The project primarily uses {primary} naming, but {inconsistent_count} classes use different conventions.",
                affected_files=list(set(i['file'] for i in naming['inconsistencies']))[:10],
                estimated_impact="Improve code consistency and readability",
                fix_suggestion=f"Standardize all class names to use {primary} convention.",
                auto_fixable=True,
            ))

        return wins

    async def _find_accessibility_wins(self) -> List[QuickWin]:
        """Find accessibility issues."""
        wins = []
        skip_dirs = {'node_modules', '.git', 'dist', 'build'}

        a11y_issues = []

        for ext in ['*.jsx', '*.tsx']:
            for file_path in self.root.rglob(ext):
                if any(skip in file_path.parts for skip in skip_dirs):
                    continue

                try:
                    content = file_path.read_text(encoding='utf-8', errors='ignore')
                    rel_path = str(file_path.relative_to(self.root))

                    # Check for images without alt
                    img_no_alt = re.findall(r'<img[^>]+(?<!alt=")[^>]*>', content)
                    if img_no_alt:
                        for match in img_no_alt[:3]:
                            if 'alt=' not in match:
                                line = content[:content.find(match)].count('\n') + 1
                                a11y_issues.append({
                                    'type': 'img-no-alt',
                                    'file': rel_path,
                                    'line': line,
                                })

                    # Check for buttons without accessible text
                    icon_only_buttons = re.findall(
                        r'<button[^>]*>\s*<(?:svg|Icon|img)[^>]*/?>\s*</button>',
                        content,
                        re.IGNORECASE
                    )
                    if icon_only_buttons:
                        a11y_issues.append({
                            'type': 'icon-button-no-label',
                            'file': rel_path,
                        })

                    # Check for click handlers on non-interactive elements
                    div_onclick = re.findall(r'<div[^>]+onClick', content)
                    if div_onclick:
                        a11y_issues.append({
                            'type': 'div-click-handler',
                            'file': rel_path,
                            'count': len(div_onclick),
                        })

                except Exception:
                    continue

        # Group issues by type
        if a11y_issues:
            img_issues = [i for i in a11y_issues if i['type'] == 'img-no-alt']
            if img_issues:
                wins.append(QuickWin(
                    type=QuickWinType.ACCESSIBILITY,
                    priority=QuickWinPriority.HIGH,
                    title=f"Found {len(img_issues)} images without alt text",
                    description="Images should have alt attributes for screen readers. Empty alt='' is acceptable for decorative images.",
                    affected_files=list(set(i['file'] for i in img_issues))[:10],
                    estimated_impact="Improve accessibility for screen reader users",
                    fix_suggestion="Add descriptive alt text to images or alt='' for decorative images.",
                    auto_fixable=False,
                ))

            div_issues = [i for i in a11y_issues if i['type'] == 'div-click-handler']
            if div_issues:
                wins.append(QuickWin(
                    type=QuickWinType.ACCESSIBILITY,
                    priority=QuickWinPriority.MEDIUM,
                    title=f"Found click handlers on div elements",
                    description="Using onClick on div elements makes them inaccessible to keyboard users. Use button or add proper ARIA attributes.",
                    affected_files=list(set(i['file'] for i in div_issues))[:10],
                    estimated_impact="Improve keyboard navigation accessibility",
                    fix_suggestion="Replace <div onClick> with <button> or add role='button' and tabIndex={0}.",
                    auto_fixable=True,
                ))

        return wins

    async def get_summary(self) -> Dict[str, Any]:
        """Get summary of all quick-wins."""
        wins = await self.find_all()

        by_type = {}
        by_priority = {}

        for win in wins:
            type_key = win.type.value
            priority_key = win.priority.value

            if type_key not in by_type:
                by_type[type_key] = 0
            by_type[type_key] += 1

            if priority_key not in by_priority:
                by_priority[priority_key] = 0
            by_priority[priority_key] += 1

        return {
            'total': len(wins),
            'by_type': by_type,
            'by_priority': by_priority,
            'auto_fixable': len([w for w in wins if w.auto_fixable]),
            'top_wins': [w.to_dict() for w in wins[:10]],
        }

    async def get_actionable_report(self) -> str:
        """Generate human-readable report of quick-wins."""
        wins = await self.find_all()

        if not wins:
            return "No quick-wins found. Your codebase looks clean!"

        lines = [
            "QUICK-WIN OPPORTUNITIES",
            "=" * 50,
            "",
        ]

        # Group by priority
        by_priority = {
            QuickWinPriority.CRITICAL: [],
            QuickWinPriority.HIGH: [],
            QuickWinPriority.MEDIUM: [],
            QuickWinPriority.LOW: [],
        }

        for win in wins:
            by_priority[win.priority].append(win)

        # Report by priority
        priority_labels = {
            QuickWinPriority.CRITICAL: "CRITICAL",
            QuickWinPriority.HIGH: "HIGH PRIORITY",
            QuickWinPriority.MEDIUM: "MEDIUM PRIORITY",
            QuickWinPriority.LOW: "LOW PRIORITY",
        }

        for priority, label in priority_labels.items():
            priority_wins = by_priority[priority]
            if not priority_wins:
                continue

            lines.extend([
                f"\n[{label}] ({len(priority_wins)} items)",
                "-" * 40,
            ])

            for i, win in enumerate(priority_wins[:5], 1):
                lines.extend([
                    f"\n{i}. {win.title}",
                    f"   {win.description[:100]}...",
                    f"   Impact: {win.estimated_impact}",
                ])
                if win.auto_fixable:
                    lines.append("   [Auto-fixable]")

            if len(priority_wins) > 5:
                lines.append(f"\n   ... and {len(priority_wins) - 5} more")

        # Summary
        lines.extend([
            "",
            "=" * 50,
            "SUMMARY",
            f"Total quick-wins: {len(wins)}",
            f"Auto-fixable: {len([w for w in wins if w.auto_fixable])}",
            "",
            "Run 'dss fix --preview' to see suggested changes.",
        ])

        return "\n".join(lines)
