"""
Tag Service - Handles vendor tag filtering and resolution for test cases.
Provides utilities for selecting which Azure DevOps test cases should run based on
execution tags (e.g. #Microsoft, #HCLTech).

NEW RULES:
    - Always skip #skip testcases.
    - Only consider tags inside the ADO 'tags' field.
    - Do NOT detect tags inside title/description/metadata.
    - If no vendor tag is provided (EXECUTION_TAG blank or missing) run all testcases except #skip.
    - #Microsoft testcases run ONLY when EXECUTION_TAG="#microsoft".
"""

import os
import re
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple


class TagService:
    """
    Service responsible for vendor tag resolution and test case filtering decisions.
    Handles tag-based test case selection logic.
    """

    CANONICAL_MICROSOFT = "#microsoft"
    CANONICAL_HCLTECH = "#hcltech"
    CANONICAL_SKIP = "#skip"

    VENDOR_TAG_ALIAS_MAP = {
        "#microsoft": CANONICAL_MICROSOFT,
        "#hcltech": CANONICAL_HCLTECH,
        "#skip": CANONICAL_SKIP,
    }

    ALLOWED_VENDOR_TAGS = {CANONICAL_MICROSOFT, CANONICAL_HCLTECH, CANONICAL_SKIP}

    VENDOR_TAG_DISPLAY_NAMES = {
        CANONICAL_MICROSOFT: "#Microsoft",
        CANONICAL_HCLTECH: "#HCLTech",
        CANONICAL_SKIP: "#skip",
    }

    TAG_TOKEN_PATTERN = re.compile(r"[@#][A-Za-z0-9_-]+")
    
    ENVIRONMENT_TAG_KEYS = (
        "EXECUTION_TAG",
        "TESTCASE_RUN_TAG",
        "TESTCASE_TAG",
        "RUN_TAG",
    )

    CLI_TAG_FLAGS = ("--tag", "--run-tag", "--execution-tag")

    def __init__(self):
        """Initialize the tag service."""
        pass

    def resolve_requested_vendor_tag(
        self,
        argv: Optional[Sequence[str]] = None,
        env: Optional[Dict[str, str]] = None,
    ) -> Optional[str]:
        """Resolve the requested vendor tag from CLI arguments or environment variables."""
        argv = list(argv or [])
        env = env if env is not None else os.environ

        cli_tag = self._parse_tag_from_argv(argv)
        if cli_tag:
            normalized_cli_tag = self._normalize_vendor_tag(cli_tag)
            if normalized_cli_tag:
                return normalized_cli_tag

        for env_key in self.ENVIRONMENT_TAG_KEYS:
            env_value = env.get(env_key)
            if not env_value:
                continue
            normalized_env_tag = self._normalize_vendor_tag(env_value)
            if normalized_env_tag:
                return normalized_env_tag

        return None

    def filter_test_cases_by_vendor_tag(
        self,
        test_cases: Iterable[Dict[str, Any]],
        requested_tag: Optional[str],
    ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
        """Filter test cases based on vendor tag, excluding #skip tagged cases."""
        normalized_tag = self._normalize_vendor_tag(requested_tag)
        test_case_list = list(test_cases or [])

        selected: List[Dict[str, Any]] = []
        excluded: List[Dict[str, Any]] = []

        for test_case in test_case_list:
            vendor_tags = self.extract_vendor_tags(test_case)

            if self.CANONICAL_SKIP in vendor_tags:
                excluded.append(test_case)
                continue

            if not normalized_tag:
                selected.append(test_case)
                continue

            if normalized_tag in vendor_tags:
                selected.append(test_case)
            else:
                excluded.append(test_case)

        return selected, excluded

    def extract_vendor_tags(self, test_case: Dict[str, Any]) -> set:
        """
        Extract ONLY allowed vendor tags (#Skip, #Microsoft, #HCLTech) from the ADO 'tags' field.
        All other tags are filtered out. Case insensitive.
        Prevent false positives from title/description/metadata.
        """
        discovered_tags: set = set()
        tags_field = test_case.get("tags")

        if isinstance(tags_field, list):
            discovered_tags = self._extract_tags_from_list(tags_field)
        elif isinstance(tags_field, str):
            discovered_tags = self._extract_tags_from_string(tags_field)

        return discovered_tags

    def _extract_tags_from_list(self, tags_list: List[Any]) -> set:
        """Extract vendor tags from a list of tag strings."""
        discovered_tags: set = set()
        for raw_tag in tags_list:
            normalized_tag = self._normalize_vendor_tag(str(raw_tag).strip())
            if normalized_tag and normalized_tag in self.ALLOWED_VENDOR_TAGS:
                discovered_tags.add(normalized_tag)
        return discovered_tags

    def _extract_tags_from_string(self, tags_string: str) -> set:
        """Extract vendor tags from a comma-separated or space-separated tag string."""
        discovered_tags: set = set()
        for token in self.TAG_TOKEN_PATTERN.findall(tags_string):
            normalized_tag = self._normalize_vendor_tag(token)
            if normalized_tag and normalized_tag in self.ALLOWED_VENDOR_TAGS:
                discovered_tags.add(normalized_tag)
        return discovered_tags

    def format_vendor_tag(self, canonical_tag: Optional[str]) -> str:
        """Format canonical tag to display name for logging."""
        if not canonical_tag:
            return ""
        return self.VENDOR_TAG_DISPLAY_NAMES.get(canonical_tag, canonical_tag.upper())

    def _parse_tag_from_argv(self, argv: Sequence[str]) -> Optional[str]:
        """Parse vendor tag from command-line arguments."""
        for idx, arg in enumerate(argv):
            tag_value = self._extract_tag_from_flag_with_equals(arg)
            if tag_value:
                return tag_value

            if arg in self.CLI_TAG_FLAGS:
                tag_value = self._extract_tag_from_next_argument(argv, idx)
                if tag_value:
                    return tag_value
        return None

    def _extract_tag_from_flag_with_equals(self, arg: str) -> Optional[str]:
        """Extract tag value from flag format: --tag=value."""
        for flag in self.CLI_TAG_FLAGS:
            if arg.startswith(f"{flag}="):
                _, tag_value = arg.split("=", 1)
                stripped_value = tag_value.strip()
                if stripped_value:
                    return stripped_value
        return None

    def _extract_tag_from_next_argument(self, argv: Sequence[str], current_index: int) -> Optional[str]:
        """Extract tag value from the next argument after a flag."""
        if current_index + 1 < len(argv):
            tag_value = argv[current_index + 1].strip()
            if tag_value:
                return tag_value
        return None

    def _normalize_vendor_tag(self, tag_value: Optional[str]) -> Optional[str]:
        """
        Normalize tag to canonical form. Only accepts #Skip, #Microsoft, #HCLTech (case insensitive).
        Returns None for any other tags.
        """
        if not tag_value:
            return None

        normalized_token = str(tag_value).strip()
        if not normalized_token:
            return None

        if normalized_token[0] not in {"@", "#"}:
            normalized_token = f"#{normalized_token}"

        if normalized_token.startswith("@"):
            return None

        normalized_token = normalized_token.lower()
        return self.VENDOR_TAG_ALIAS_MAP.get(normalized_token)

    def filter_test_case_tags(self, test_case: Dict[str, Any]) -> Dict[str, Any]:
        """
        Filter tags in a test case to only keep #Skip, #Microsoft, #HCLTech (case insensitive).
        Removes all other tags from the test case object.
        Preserves vendor id (test case ID) and all other fields.
        """
        filtered_case = test_case.copy()
        allowed_tags = self.extract_vendor_tags(test_case)
        display_tag_list = self._convert_to_display_tags(allowed_tags)

        filtered_case["tags"] = display_tag_list

        if "metadata" in filtered_case and isinstance(filtered_case["metadata"], dict):
            filtered_case["metadata"] = filtered_case["metadata"].copy()
            filtered_case["metadata"]["tags"] = display_tag_list

        return filtered_case

    def _convert_to_display_tags(self, canonical_tags: set) -> List[str]:
        """Convert canonical tags to display format."""
        return [
            self.VENDOR_TAG_DISPLAY_NAMES.get(canonical_tag, canonical_tag)
            for canonical_tag in canonical_tags
        ]


# Maintain backward compatibility with function-based API
_tag_service_instance = TagService()


def resolve_requested_vendor_tag(
    argv: Optional[Sequence[str]] = None,
    env: Optional[Dict[str, str]] = None,
) -> Optional[str]:
    """Backward compatibility function - resolve requested vendor tag."""
    return _tag_service_instance.resolve_requested_vendor_tag(argv, env)


def filter_test_cases_by_vendor_tag(
    test_cases: Iterable[Dict[str, Any]],
    requested_tag: Optional[str],
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
    """Backward compatibility function - filter test cases by vendor tag."""
    return _tag_service_instance.filter_test_cases_by_vendor_tag(test_cases, requested_tag)


def extract_vendor_tags(test_case: Dict[str, Any]) -> set:
    """Backward compatibility function - extract vendor tags."""
    return _tag_service_instance.extract_vendor_tags(test_case)


def format_vendor_tag(canonical_tag: Optional[str]) -> str:
    """Backward compatibility function - format vendor tag."""
    return _tag_service_instance.format_vendor_tag(canonical_tag)


def filter_test_case_tags(test_case: Dict[str, Any]) -> Dict[str, Any]:
    """Backward compatibility function - filter test case tags."""
    return _tag_service_instance.filter_test_case_tags(test_case)


__all__ = [
    "TagService",
    "resolve_requested_vendor_tag",
    "filter_test_cases_by_vendor_tag",
    "extract_vendor_tags",
    "format_vendor_tag",
    "filter_test_case_tags",
]
