"""
Note command for posting notes to OpenReview.
"""

import json
import os
import re
from argparse import ArgumentParser, Namespace
from pathlib import Path

from openreview.api import Note

from ..client import get_client
from ..command import Command
from ..custom_stages import get_all_stage_definitions
from ..log import log
from ..qlever import query_results_by_recipient


class NoteCommand(Command):
    """
    Post notes to OpenReview from a JSON template.
    """

    @property
    def name(self) -> str:
        return "note"

    @property
    def help(self) -> str:
        return "Post notes to OpenReview from a JSON template"

    def add_arguments(self, parser: ArgumentParser) -> None:
        parser.add_argument(
            "--add",
            metavar="JSON_FILE",
            help="Post notes from a JSON template file",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be done without posting notes",
        )

    def _get_stage_definition(self, invitation_name: str) -> dict | None:
        """Look up stage definition by invitation name."""
        for stage_def in get_all_stage_definitions():
            if stage_def.get("name") == invitation_name:
                return stage_def
        return None

    def _build_invitation_id(
        self, venue_id: str, invitation_name: str, stage_def: dict | None
    ) -> str:
        """Build the full invitation ID."""
        if stage_def:
            committee = stage_def.get("committee", "Authors")
            if committee.lower() == "authors":
                return f"{venue_id}/Authors/-/{invitation_name}"
            else:
                # Map committee names to group names
                committee_map = {
                    "reviewers": "Reviewers",
                    "area_chairs": "Area_Chairs",
                    "senior_area_chairs": "Senior_Area_Chairs",
                }
                group = committee_map.get(committee.lower(), committee)
                return f"{venue_id}/{group}/-/{invitation_name}"
        # Default to Authors
        return f"{venue_id}/Authors/-/{invitation_name}"

    def _substitute_placeholders(self, value: str, row_data: dict) -> str:
        """Substitute {{placeholder}} patterns in a string."""
        result = value
        for var, val in row_data.items():
            result = result.replace("{{" + var + "}}", val if val else "")
        return result

    def _substitute_in_note(self, note_template: dict, row_data: dict) -> dict:
        """Recursively substitute placeholders in the note template."""
        result = {}
        for key, value in note_template.items():
            if isinstance(value, str):
                result[key] = self._substitute_placeholders(value, row_data)
            elif isinstance(value, list):
                result[key] = [
                    self._substitute_placeholders(item, row_data)
                    if isinstance(item, str)
                    else item
                    for item in value
                ]
            elif isinstance(value, dict):
                result[key] = self._substitute_in_note(value, row_data)
            else:
                result[key] = value
        return result

    def execute(self, args: Namespace) -> None:
        if not args.add:
            log.error("Please specify --add JSON_FILE")
            return

        # Load the note template
        template_path = Path(args.add)
        if not template_path.exists():
            log.error(f"Template file not found: {template_path}")
            return

        with open(template_path) as f:
            template = json.load(f)

        # Extract template components
        invitation_name = template.get("invitation")
        if not invitation_name:
            log.error("Template must have 'invitation' field")
            return

        parameters = template.get("parameters", {})
        note_template = template.get("note", {})
        if not note_template:
            log.error("Template must have 'note' field")
            return

        # Get SPARQL query URL
        sparql_query_url = parameters.get("sparql-query")
        if not sparql_query_url:
            log.error("Template parameters must have 'sparql-query' field")
            return

        # Get venue ID
        venue_id = args.venue_id
        if not venue_id:
            log.error("OPENREVIEW_VENUE_ID not set")
            return

        # Look up stage definition and build invitation ID
        stage_def = self._get_stage_definition(invitation_name)
        invitation_id = self._build_invitation_id(venue_id, invitation_name, stage_def)

        log.info(f"Invitation: {invitation_id}")
        log.info(f"SPARQL query: {sparql_query_url}")

        # Fetch SPARQL query results
        try:
            recipients, data_by_recipient = query_results_by_recipient(sparql_query_url)
        except Exception as e:
            log.error(f"Failed to fetch SPARQL query results: {e}")
            return

        if not recipients:
            log.warning("No recipients found")
            return

        # Show complete list of values for each parameter
        # Get all parameter names (excluding sparql-query)
        param_names = [p for p in parameters.keys() if p != "sparql-query"]
        for param_name in param_names:
            # Collect all values for this parameter from the query results
            values = [
                data_by_recipient.get(r, {}).get(param_name, r)
                for r in recipients
            ]
            log.info(f"\n{param_name} ({len(values)}): {', '.join(values)}")

        # Show sample of what will be posted
        sample_recipient = recipients[0]
        sample_data = data_by_recipient.get(sample_recipient, {})
        sample_note = self._substitute_in_note(note_template, sample_data)
        log.info(f"\nSample note for {sample_recipient}:")
        log.info(f"  signatures: {sample_note.get('signatures')}")
        for key, value in sample_note.items():
            if key != "signatures":
                display_value = value[:50] + "..." if len(str(value)) > 50 else value
                log.info(f"  {key}: {display_value}")

        if args.dry_run:
            log.warning(
                f"\nDry run: would post {len(recipients)} notes to {invitation_id}"
            )
            return

        # Confirm before posting
        try:
            input(
                f"\n\033[94mPress CTRL+C to abort, RETURN to post {len(recipients)} "
                f"notes to {invitation_id}\033[0m "
            )
        except KeyboardInterrupt:
            log.warning("\nAborted")
            return

        # Post notes
        client = get_client()
        posted_count = 0
        failed_count = 0
        skipped_count = 0

        for recipient in recipients:
            row_data = data_by_recipient.get(recipient, {})
            note_data = self._substitute_in_note(note_template, row_data)

            signatures = note_data.get("signatures", [])
            if not signatures:
                log.warning(f"No signatures for {recipient}, skipping")
                skipped_count += 1
                continue

            # Build content with proper {value: ...} structure
            content = {}
            for key, value in note_data.items():
                if key != "signatures":
                    content[key] = {"value": value}

            try:
                client.post_note_edit(
                    invitation=invitation_id,
                    signatures=signatures,
                    note=Note(content=content),
                )
                posted_count += 1
            except Exception as e:
                log.warning(f"Failed to post note for {recipient}: {e}")
                failed_count += 1

        # Report results
        log.info(f"\nPosted: {posted_count}, Failed: {failed_count}, Skipped: {skipped_count}")
