#!/usr/bin/env python3
"""
PPT Master - Error Message Helper

Provides user-friendly error messages and specific fix suggestions.
"""

import argparse
from typing import Dict, List, Optional

from console_encoding import configure_utf8_stdio

configure_utf8_stdio()


class ErrorHelper:
    """Error message helper."""

    # Error types and their corresponding fix suggestions
    ERROR_SOLUTIONS = {
        'missing_readme': {
            'message': 'Missing README.md file',
            'solutions': [
                'Create a README.md file with project description, usage instructions, etc.',
                'Reference template: examples/google_annual_report_ppt169_20251116/README.md',
                'Or use command: cp examples/google_annual_report_ppt169_20251116/README.md <your_project>/'
            ],
            'severity': 'error'
        },
        'missing_spec': {
            'message': 'Missing design specification file',
            'solutions': [
                'Create a design_spec.md file',
                'Include: canvas specs, color scheme, font specs, layout specs, content outline',
                'Refer to the design specification generated by the Strategist role'
            ],
            'severity': 'warning'
        },
        'missing_svg_output': {
            'message': 'Missing svg_output directory',
            'solutions': [
                'Create the svg_output directory: mkdir svg_output',
                'Place generated SVG files in this directory',
                'Ensure SVG files follow naming convention: slide_XX_name.svg'
            ],
            'severity': 'error'
        },
        'empty_svg_output': {
            'message': 'svg_output directory is empty',
            'solutions': [
                'Use the AI role (Executor) to generate SVG files',
                'Save SVG files to the svg_output directory',
                'Ensure file naming format: slide_01_cover.svg, slide_02_content.svg, etc.'
            ],
            'severity': 'warning'
        },
        'invalid_svg_naming': {
            'message': 'Non-standard SVG file naming',
            'solutions': [
                'Rename SVG files using format: slide_XX_name.svg',
                'XX should be a two-digit number (01, 02, ...)',
                'name should use English or pinyin, separated by underscores',
                'Example: slide_01_cover.svg, slide_02_overview.svg'
            ],
            'severity': 'warning'
        },
        'missing_project_date': {
            'message': 'Project directory missing date suffix',
            'solutions': [
                'Rename the project directory to add a date suffix: _YYYYMMDD',
                'Format: {project_name}_{format}_{YYYYMMDD}',
                'Example: my_project_ppt169_20251116',
                'Command: mv old_name new_name_ppt169_20251116'
            ],
            'severity': 'warning'
        },
        'viewbox_mismatch': {
            'message': 'SVG viewBox differs from the recorded canvas format',
            'solutions': [
                'Check the viewBox attribute of SVG files',
                'Treat the root viewBox as the actual canvas size',
                'If the project metadata is stale, export will use the SVG viewBox',
                'PPT 16:9 should be: viewBox="0 0 1280 720"',
                'PPT 4:3 should be: viewBox="0 0 1024 768"',
                'Reference: references/canvas-formats.md'
            ],
            'severity': 'warning'
        },
        'multiple_viewboxes': {
            'message': 'Multiple different viewBox settings detected',
            'solutions': [
                'Unify the viewBox across all SVG files',
                'All pages in the same project should use the same canvas size',
                'Use find-and-replace tools for batch correction',
                'Reference the viewBox setting of the first page'
            ],
            'severity': 'warning'
        },
        'no_viewbox': {
            'message': 'SVG file missing viewBox attribute',
            'solutions': [
                'Add the viewBox attribute to the SVG root element',
                'Format: <svg viewBox="0 0 1280 720" ...>',
                'Root width/height are optional compatibility attributes',
                'The root viewBox is mandatory for SVG generation'
            ],
            'severity': 'error'
        },
        'foreignobject_detected': {
            'message': 'Forbidden <foreignObject> element detected',
            'solutions': [
                'Remove <foreignObject> elements',
                'Use <text> + <tspan> for manual line wrapping',
                'This is a project technical specification requirement',
                'Reference: references/shared-standards.md'
            ],
            'severity': 'error'
        },
        'clippath_on_non_image': {
            'message': 'clip-path is only allowed on <image> elements',
            'solutions': [
                'Remove clip-path from shapes / groups / text',
                'Draw the target geometry directly with the matching native element: <circle> / <ellipse> / <rect rx="..."> / <polygon> / <path>. A rect clipped to a circle is just a <circle>.',
                'clip-path on <image> is conditionally allowed — see references/shared-standards.md §1.2'
            ],
            'severity': 'error'
        },
        'clippath_def_missing': {
            'message': 'clip-path references a <clipPath> id that does not exist in <defs>',
            'solutions': [
                'Define the referenced <clipPath id="..."> inside <defs>',
                'The clipPath must contain exactly one shape child (circle / ellipse / rect with rx,ry / path / polygon)',
                'Reference: references/shared-standards.md §1.2'
            ],
            'severity': 'error'
        },
        'mask_detected': {
            'message': 'Forbidden <mask> element detected',
            'solutions': [
                'Remove <mask> elements',
                'PPT does not support SVG masks',
                'Use opacity (opacity/fill-opacity) as an alternative'
            ],
            'severity': 'error'
        },
        'style_element_detected': {
            'message': 'Forbidden <style> element detected',
            'solutions': [
                'Remove <style> elements',
                'Convert CSS styles to inline attributes',
                'Example: fill="#000" instead of class="text-black"'
            ],
            'severity': 'error'
        },
        'class_attribute_detected': {
            'message': 'Forbidden class attribute detected',
            'solutions': [
                'Remove all class attributes',
                'Use inline styles instead',
                'Example: fill="#000" stroke="#333" directly on the element'
            ],
            'severity': 'error'
        },
        'id_attribute_detected': {
            'message': 'Forbidden CSS selector usage with id detected',
            'solutions': [
                'Keep IDs for local references or documented semantic/animation groups',
                'Remove <style> rules and CSS selectors',
                'Use inline presentation attributes instead'
            ],
            'severity': 'error'
        },
        'external_css_detected': {
            'message': 'Forbidden external CSS reference detected',
            'solutions': [
                'Remove <?xml-stylesheet?> declarations',
                'Remove <link rel="stylesheet"> references',
                'Remove @import external styles',
                'Convert styles to inline attributes'
            ],
            'severity': 'error'
        },
        # Note: <marker> and marker-end are NO LONGER forbidden — they are
        # conditionally allowed (see references/shared-standards.md §1.1).
        # The converter maps qualifying markers to native DrawingML arrow heads.
        'marker_orphan_ref': {
            'message': 'marker-start/marker-end references a marker id, but no <marker> element is defined',
            'solutions': [
                'Define the <marker> inside <defs>',
                'Or remove the marker-start/marker-end attribute',
                'See shared-standards.md §1.1 for marker constraints',
            ],
            'severity': 'error'
        },
        'event_attribute_detected': {
            'message': 'Forbidden event attribute detected',
            'solutions': [
                'Remove onclick/onload and other event attributes',
                'Scripts and event handling are forbidden in SVG',
                'Implement interactivity in PPT instead'
            ],
            'severity': 'error'
        },
        'set_detected': {
            'message': 'Forbidden <set> element detected',
            'solutions': [
                'Remove <set> elements',
                'SVG animations will not be exported to PPT',
                'Use PPT native animations for animation effects'
            ],
            'severity': 'error'
        },
        'iframe_detected': {
            'message': 'Forbidden <iframe> element detected',
            'solutions': [
                'Remove <iframe> elements',
                'External pages should not be embedded in SVG'
            ],
            'severity': 'error'
        },
        'textpath_detected': {
            'message': 'Forbidden <textPath> element detected',
            'solutions': [
                'Remove <textPath> elements',
                'Text on path is not compatible with PPT',
                'Use regular <text> elements and adjust position manually'
            ],
            'severity': 'error'
        },
        'webfont_detected': {
            'message': 'Forbidden web font (@font-face) detected',
            'solutions': [
                'Remove @font-face declarations',
                'Use font-family stacks that export PPT-safe pre-installed typefaces',
                'Example: font-family: "Microsoft YaHei", Arial, sans-serif'
            ],
            'severity': 'error'
        },
        'animation_detected': {
            'message': 'Forbidden SMIL animation element detected',
            'solutions': [
                'Remove all <animate>, <animateMotion>, <animateTransform> and similar elements',
                'SVG animations will not be exported to PPT',
                'Use PPT native animations for animation effects'
            ],
            'severity': 'error'
        },
        'script_detected': {
            'message': 'Forbidden <script> element detected',
            'solutions': [
                'Remove <script> elements',
                'Scripts and event handling are forbidden',
                'JavaScript in SVG will not execute in PPT'
            ],
            'severity': 'error'
        },
        'invalid_font': {
            'message': 'Font stack exports non-PPT-safe typefaces to PPTX',
            'solutions': [
                'Use stacks whose exported Latin / EA typefaces are pre-installed',
                'CJK: "Microsoft YaHei", sans-serif  |  SimSun, serif',
                'Latin: Arial, sans-serif  |  "Times New Roman", serif',
                'Mono: Consolas, "Courier New", monospace',
                'See strategist.md §g for the full PPT-safe discipline'
            ],
            'severity': 'warning'
        }
    }

    @classmethod
    def get_solution(cls, error_type: str, context: Optional[Dict] = None) -> Dict:
        """
        Get the solution for an error.

        Args:
            error_type: Error type
            context: Context information (optional)

        Returns:
            Dictionary containing message, solutions, severity
        """
        if error_type in cls.ERROR_SOLUTIONS:
            solution = cls.ERROR_SOLUTIONS[error_type].copy()

            # Customize message based on context
            if context:
                solution = cls._customize_solution(solution, context)

            return solution

        # Unknown error type
        return {
            'message': 'Unknown error',
            'solutions': ['Please check the documentation or contact the maintainer'],
            'severity': 'error'
        }

    @classmethod
    def _customize_solution(cls, solution: Dict, context: Dict) -> Dict:
        """
        Customize solution based on context.

        Args:
            solution: Original solution
            context: Context information

        Returns:
            Customized solution
        """
        customized = solution.copy()

        # Customize based on project path
        if 'project_path' in context:
            project_path = context['project_path']
            customized['solutions'] = [
                s.replace('<project_path>', project_path).replace(
                    '<your_project>', project_path)
                for s in customized['solutions']
            ]

        # Customize based on filename
        if 'file_name' in context:
            file_name = context['file_name']
            customized['message'] = f"{customized['message']}: {file_name}"

        # Customize based on expected/actual values
        if 'expected' in context and 'actual' in context:
            customized['message'] += f" (expected: {context['expected']}, actual: {context['actual']})"

        return customized

    @classmethod
    def format_error_message(cls, error_type: str, context: Optional[Dict] = None) -> str:
        """
        Format error message (for terminal output).

        Args:
            error_type: Error type
            context: Context information

        Returns:
            Formatted error message string
        """
        solution = cls.get_solution(error_type, context)

        lines = []

        # Error message
        severity_icon = "[ERROR]" if solution['severity'] == 'error' else "[WARN]"
        lines.append(f"{severity_icon} {solution['message']}")

        # Solutions
        if solution['solutions']:
            lines.append("\nSuggested fixes:")
            for i, sol in enumerate(solution['solutions'], 1):
                lines.append(f"   {i}. {sol}")

        return "\n".join(lines)

    @classmethod
    def print_error(cls, error_type: str, context: Optional[Dict] = None):
        """
        Print formatted error message.

        Args:
            error_type: Error type
            context: Context information
        """
        print(cls.format_error_message(error_type, context))

    @classmethod
    def get_all_error_types(cls) -> List[str]:
        """Get all supported error types."""
        return list(cls.ERROR_SOLUTIONS.keys())

    @classmethod
    def print_help(cls):
        """Print all error types and solutions."""
        print("PPT Master - Error Types and Solutions\n")
        print("=" * 80)

        for error_type, info in cls.ERROR_SOLUTIONS.items():
            print(f"\n[{error_type}]")
            print(f"Message: {info['message']}")
            print(f"Severity: {info['severity']}")
            print("Solutions:")
            for i, sol in enumerate(info['solutions'], 1):
                print(f"  {i}. {sol}")
            print("-" * 80)


def build_parser() -> argparse.ArgumentParser:
    """Build the command-line parser."""
    parser = argparse.ArgumentParser(
        description="Look up PPT Master error messages and suggested fixes.",
    )
    parser.add_argument(
        "error_type",
        nargs="?",
        choices=sorted(ErrorHelper.ERROR_SOLUTIONS),
        help="Error type to explain",
    )
    parser.add_argument(
        "context",
        nargs="*",
        metavar="key=value",
        help="Optional context values used by templates",
    )
    return parser


def main(argv: list[str] | None = None) -> int:
    """Run the CLI entry point for error lookup."""
    parser = build_parser()
    args = parser.parse_args(argv)

    if not args.error_type:
        ErrorHelper.print_help()
        return 0

    context = {}
    for item in args.context:
        if '=' not in item:
            parser.error(f"context values must use key=value syntax: {item}")
        key, value = item.split('=', 1)
        context[key] = value

    print(ErrorHelper.format_error_message(args.error_type, context))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
