"""NVMe CLI utility support for disks that don't work well with smartctl."""

import re
import sys
from dataclasses import dataclass
from typing import Optional, Tuple

from jusfltuls.shell_calls import get_debug_mode


@dataclass
class NvmeSelfTestLogResult:
    """Parsed result from `nvme self-test-log`."""

    last_short_hours_ago: Optional[int] = None
    last_short_failed: bool = False
    last_long_hours_ago: Optional[int] = None
    last_long_failed: bool = False
    short_in_progress: bool = False
    short_remaining_pct: Optional[int] = None
    long_in_progress: bool = False
    long_remaining_pct: Optional[int] = None


def parse_nvme_smart_log(output: str) -> Tuple[str, Optional[int], Optional[int]]:
    """Parse `nvme smart-log` output to extract health, errors, and power-on hours.

    Args:
        output: Raw output from `nvme smart-log`

    Returns:
        Tuple of (overall_health, power_on_hours, critical_warning)
        overall_health: "PASSED", "FAILED", or "UNKNOWN"
        power_on_hours: Power-on hours or None if not found
        critical_warning: Critical warning value (0 = OK, non-zero = errors) or None
    """
    overall_health = "UNKNOWN"
    power_on_hours = None
    critical_warning = None

    for line in output.split("\n"):
        line = line.strip()

        # Parse critical warning (bitmask indicating various error conditions)
        # Bit 0: Available spare space has fallen below threshold
        # Bit 1: Temperature above threshold or below threshold
        # Bit 2: NVM subsystem reliability has been degraded
        # Bit 3: Media has been placed in read only mode
        # Bit 4: Volatile memory backup device has failed
        # Bit 5-7: Reserved
        if "critical_warning" in line.lower() or "critical warning" in line.lower():
            # Try to match hex format first: "0x00" or "0x01"
            match = re.search(r"0x([0-9a-fA-F]+)", line)
            if match:
                try:
                    critical_warning = int(match.group(1), 16)
                except ValueError:
                    pass
            else:
                # Try decimal format
                match = re.search(r"[:\s]+(\d+)", line)
                if match:
                    try:
                        critical_warning = int(match.group(1))
                    except ValueError:
                        pass

        # Parse power on hours
        if "power_on_hours" in line.lower() or "power on hours" in line.lower():
            # Try hex format first (0x...)
            match = re.search(r":\s*0x([0-9a-fA-F]+)", line)
            if match:
                try:
                    power_on_hours = int(match.group(1), 16)
                except ValueError:
                    pass
            else:
                # Try decimal format
                # Format: "power_on_hours     : 6474" or "power on hours: 6474"
                # May include commas or periods as thousand separators
                match = re.search(r":[\s]+([\d,. ]+)", line)
                if match:
                    try:
                        raw = match.group(1).strip()
                        power_on_hours = int(
                            raw.replace(",", "").replace(".", "").replace(" ", "")
                        )
                    except ValueError:
                        pass

    # Determine health based on critical warning
    # critical_warning is a bitmask - any non-zero value indicates a problem
    if critical_warning is not None:
        if critical_warning == 0:
            overall_health = "PASSED"
        else:
            overall_health = "FAILED"

    return overall_health, power_on_hours, critical_warning


def parse_nvme_device_self_test_status(output: str) -> NvmeSelfTestLogResult:
    """Parse `nvme device-self-test -s 0h` output to query current status.

    The output can have several formats depending on nvme-cli version:

    Format 1 (newer):
    Device self-test command completed successfully
    Current operation: 0x1 (Short device self-test)
    Current completion: 50%

    Format 2 (older):
    Device Self-test operation in progress (76% complete)
    Self-test Code: 0x1 (Short)

    Format 3 (minimal):
    progress 76%

    Or when no test is running:
    Current operation: 0x0 (No self-test operation in progress)

    Args:
        output: Raw output from `nvme device-self-test -s 0h`

    Returns:
        NvmeSelfTestLogResult with current test progress information
    """
    # Debug output
    from jusfltuls.shell_calls import get_debug_mode
    import sys

    if get_debug_mode():
        print(f"\n[DEBUG] parse_nvme_device_self_test_status input:\n{repr(output)}", file=sys.stderr, flush=True)

    result = NvmeSelfTestLogResult()

    current_operation = None
    current_completion = None

    for line in output.split("\n"):
        line = line.strip()

        if get_debug_mode():
            print(f"[DEBUG]   Parsing line: {repr(line)}", file=sys.stderr, flush=True)

        # Parse current operation - Format 1
        # Format: "Current operation: 0x1 (Short device self-test)"
        # or:     "Current operation: 0x2 (Extended device self-test)"
        # or:     "Current operation: 0x0 (No self-test operation in progress)"
        if "current operation" in line.lower():
            if "0x1" in line or "short" in line.lower():
                current_operation = "short"
            elif "0x2" in line or "extended" in line.lower() or "long" in line.lower():
                current_operation = "long"
            elif "0x0" in line or "no self-test" in line.lower():
                current_operation = None
            if get_debug_mode():
                print(f"[DEBUG]     Found operation: {current_operation}", file=sys.stderr, flush=True)

        # Parse operation from "Self-test Code:" line - Format 2
        if "self-test code" in line.lower():
            if "0x1" in line or "short" in line.lower():
                current_operation = "short"
            elif "0x2" in line or "extended" in line.lower() or "long" in line.lower():
                current_operation = "long"
            if get_debug_mode():
                print(f"[DEBUG]     Found self-test code: {current_operation}", file=sys.stderr, flush=True)

        # Parse operation from "Device Self-test operation in progress" - Format 2
        if "device self-test operation in progress" in line.lower():
            if "short" in line.lower():
                current_operation = "short"
            elif "extended" in line.lower() or "long" in line.lower():
                current_operation = "long"
            else:
                # Default to short if not specified
                current_operation = "short"
            # Try to extract percentage from this line
            match = re.search(r"(\d+)%", line)
            if match:
                current_completion = int(match.group(1))
            if get_debug_mode():
                print(f"[DEBUG]     Found in-progress operation: {current_operation}, completion: {current_completion}", file=sys.stderr, flush=True)

        # Parse current completion/progress percentage
        # Formats: "Current completion: 50%" or "progress 76%" or "76% complete"
        if ("completion" in line.lower() or "progress" in line.lower()) and "%" in line:
            match = re.search(r"(\d+)%", line)
            if match:
                current_completion = int(match.group(1))
                if get_debug_mode():
                    print(f"[DEBUG]     Found completion: {current_completion}%", file=sys.stderr, flush=True)

    if get_debug_mode():
        print(f"[DEBUG]   Final: operation={current_operation}, completion={current_completion}", file=sys.stderr, flush=True)

    # Set in-progress status based on parsed information
    # NOTE: For nvme device-self-test, "progress X%" means X% REMAINING, not X% complete
    # So we use the value directly without conversion
    if current_operation == "short":
        result.short_in_progress = True
        if current_completion is not None:
            # For device-self-test, progress means remaining (not complete)
            result.short_remaining_pct = current_completion
    elif current_operation == "long":
        result.long_in_progress = True
        if current_completion is not None:
            # For device-self-test, progress means remaining (not complete)
            result.long_remaining_pct = current_completion
    elif current_completion is not None and current_operation is None:
        # We found a completion percentage but no test type
        # Mark this as "unknown test in progress" - will be resolved by self-test-log
        # For now, store the completion in a temporary field that signals a test is running
        # The actual test type will be determined by merging with self-test-log
        result.short_in_progress = True  # Temporary flag, type to be determined
        result.short_remaining_pct = current_completion  # Device-self-test progress = remaining
        if get_debug_mode():
            print(f"[DEBUG]   Found progress without test type - will determine type from self-test-log", file=sys.stderr, flush=True)

    if get_debug_mode():
        print(f"[DEBUG]   Result: short_in_progress={result.short_in_progress}, short_remaining_pct={result.short_remaining_pct}, long_in_progress={result.long_in_progress}, long_remaining_pct={result.long_remaining_pct}", file=sys.stderr, flush=True)

    return result


def parse_nvme_self_test_log(
    output: str, power_on_hours: Optional[int]
) -> NvmeSelfTestLogResult:
    """Parse `nvme self-test-log` output.

    Actual NVMe format:
    Device Self Test Log for NVME device:nvme0
    Current operation  : 0
    Current Completion : 0%
    Self Test Result[0]:
      Operation Result             : 0
      Self Test Code               : 1
      Valid Diagnostic Information : 0
      Power on hours (POH)         : 0x1950
      Vendor Specific              : 0 0
    Self Test Result[1]:
      ...

    Current operation: 0=none, 1=short, 2=extended
    Self Test Code: 1=short, 2=extended
    Operation Result: 0=success, non-zero=failed
    POH is in hex format (0x1950)

    Args:
        output: Raw output from `nvme self-test-log`
        power_on_hours: Current power-on hours for calculating hours-ago

    Returns:
        NvmeSelfTestLogResult with parsed information
    """
    result = NvmeSelfTestLogResult()

    # Check for current operation in progress
    current_operation_code = None
    current_completion = None

    for line in output.split("\n"):
        line = line.strip()

        # Parse "Current operation  : 1" or "Current operation  : 2"
        if line.startswith("Current operation") and ":" in line:
            match = re.search(r":\s*(\d+)", line)
            if match:
                current_operation_code = int(match.group(1))

        # Parse "Current Completion : 50%"
        if line.startswith("Current Completion") and ":" in line:
            match = re.search(r"(\d+)%", line)
            if match:
                current_completion = int(match.group(1))

    # Set in-progress status based on current operation code
    # 0 = no operation, 1 = short, 2 = extended/long
    if current_operation_code == 1:
        result.short_in_progress = True
        if current_completion is not None:
            # Current Completion shows percentage COMPLETE, convert to remaining
            result.short_remaining_pct = 100 - current_completion
    elif current_operation_code == 2:
        result.long_in_progress = True
        if current_completion is not None:
            # Current Completion shows percentage COMPLETE, convert to remaining
            result.long_remaining_pct = 100 - current_completion

    # Parse self-test result entries - only look at first entry of each type
    # Each Self Test Result[N] block contains multiple lines
    entries = []
    current_entry = {}

    for line in output.split("\n"):
        line = line.strip()

        # Start of a new result entry
        if line.startswith("Self Test Result["):
            if current_entry:
                entries.append(current_entry)
                if get_debug_mode():
                    print(f"[DEBUG] Parsed entry: {current_entry}", file=sys.stderr, flush=True)
            current_entry = {}

        # Parse "Operation Result             : 0"
        if line.startswith("Operation Result") and ":" in line:
            match = re.search(r":\s*(\d+)", line)
            if match:
                result_code = int(match.group(1))
                current_entry["failed"] = result_code != 0

        # Parse "Self Test Code               : 1" or ": 2"
        if line.startswith("Self Test Code") and ":" in line:
            match = re.search(r":\s*(\d+)", line)
            if match:
                test_code = int(match.group(1))
                if test_code == 1:
                    current_entry["type"] = "short"
                elif test_code == 2:
                    current_entry["type"] = "long"

        # Parse "Power on hours (POH)         : 0x1950" (HEX format!)
        if "power on hours" in line.lower() and "poh" in line.lower() and ":" in line:
            if get_debug_mode():
                print(f"[DEBUG] Found POH line: {repr(line)}", file=sys.stderr, flush=True)
            # Try hex format first (0x...)
            match = re.search(r":\s*0x([0-9a-fA-F]+)", line)
            if match:
                try:
                    # Convert from hex to decimal
                    current_entry["hours"] = int(match.group(1), 16)
                    if get_debug_mode():
                        print(f"[DEBUG] Parsed hex POH: {current_entry['hours']}", file=sys.stderr, flush=True)
                except ValueError as e:
                    if get_debug_mode():
                        print(f"[DEBUG] Failed to parse hex POH: {e}", file=sys.stderr, flush=True)
            else:
                # Try decimal format as fallback
                match = re.search(r":\s*(\d+)", line)
                if match:
                    try:
                        current_entry["hours"] = int(match.group(1))
                        if get_debug_mode():
                            print(f"[DEBUG] Parsed decimal POH: {current_entry['hours']}", file=sys.stderr, flush=True)
                    except ValueError as e:
                        if get_debug_mode():
                            print(f"[DEBUG] Failed to parse decimal POH: {e}", file=sys.stderr, flush=True)
                elif get_debug_mode():
                    print(f"[DEBUG] No regex match for POH value in line", file=sys.stderr, flush=True)

    # Don't forget the last entry
    if current_entry:
        entries.append(current_entry)
        if get_debug_mode():
            print(f"[DEBUG] Parsed final entry: {current_entry}", file=sys.stderr, flush=True)

    # Process entries to find the most recent completed tests
    # Only look at first occurrence of each test type (most recent)
    short_done = False
    long_done = False

    for entry in entries:
        test_type = entry.get("type")
        test_hours = entry.get("hours")
        test_failed = entry.get("failed", False)

        if test_type == "short" and not short_done and not result.short_in_progress:
            if power_on_hours is not None and test_hours is not None:
                result.last_short_hours_ago = power_on_hours - test_hours
            result.last_short_failed = test_failed
            short_done = True

        elif test_type == "long" and not long_done and not result.long_in_progress:
            if power_on_hours is not None and test_hours is not None:
                result.last_long_hours_ago = power_on_hours - test_hours
            result.last_long_failed = test_failed
            long_done = True

        # Stop after finding first of each type
        if short_done and long_done:
            break

    return result


def is_nvme_device(device: str) -> bool:
    """Check if a device is an NVMe device.

    Args:
        device: Device path (e.g., /dev/nvme0n1)

    Returns:
        True if device is NVMe, False otherwise
    """
    return "nvme" in device.lower()


def check_nvme_cli_available() -> bool:
    """Check if nvme CLI utility is available.

    Returns:
        True if nvme command is available, False otherwise
    """
    from jusfltuls.shell_calls import run_command

    returncode, _ = run_command(["which", "nvme"], timeout=5)
    return returncode == 0
