from enum import Enum
from typing import List, Optional, Set, Union

import pydantic

Match = List[List[int]]


class EntanglementAnalysisStatus(str, Enum):
    SUCCESS = "success"
    TIMEOUT = "timeout"
    ERROR = "error"


class EntanglementAnalysisResult(pydantic.BaseModel):
    status: EntanglementAnalysisStatus
    details: Union[pydantic.NonNegativeInt, str]


class AnalysisStatus(str, Enum):
    NONE = "none"
    SUCCESS = "success"
    CANCELLED = "cancelled"
    ERROR = "error"


class BasisGates(str, Enum):
    CX = "cx"
    CY = "cy"
    CZ = "cz"
    U = "u"
    U2 = "u2"
    P = "p"


class QuantumCircuitProperties(pydantic.BaseModel):
    depth: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Circuit depth"
    )
    auxiliary_qubits: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Number of Auxiliary qubits"
    )
    classical_bits: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Number of classical bits"
    )
    gates_count: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Total number of gates in the circuit"
    )
    multi_qubit_gates_count: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Number of multi-qubit gates in circuit"
    )
    non_entangled_subcircuits_count: pydantic.NonNegativeInt = pydantic.Field(
        default=..., description="Number of non-entangled sub-circuit "
    )
    entanglement_upper_bound: EntanglementAnalysisResult = pydantic.Field(
        default=...,
        description="An upper bound to the entanglement (measured by the Schmidt rank width) of states that can"
        "generated by the circuit. None is returned if the entanglement analysis took too long to complete",
    )


class NativeQuantumCircuitProperties(QuantumCircuitProperties):
    native_gates: Set[BasisGates] = pydantic.Field(
        default=..., description="Native gates used for decomposition"
    )


class PatternRecognitionResult(pydantic.BaseModel):
    found_patterns: List[str]


class PatternMatchingResult(pydantic.BaseModel):
    found_patterns: List[str]


class Circuit(pydantic.BaseModel):
    closed_circuit_qasm: str
    image: str


class PatternAnalysis(pydantic.BaseModel):
    pattern_matching: Optional[PatternMatchingResult] = pydantic.Field(
        default=..., description="Pattern matching algorithm"
    )
    pattern_recognition: Optional[PatternRecognitionResult] = pydantic.Field(
        default=..., description="Find unknown patterns"
    )
    circuit: Circuit = pydantic.Field(
        default=..., description="Quantum circuit after pattern analysis"
    )


class Analysis(pydantic.BaseModel):
    input_properties: QuantumCircuitProperties = pydantic.Field(
        default=..., description="Input circuit properties"
    )
    native_properties: NativeQuantumCircuitProperties = pydantic.Field(
        default=..., description="Transpiled circuit properties"
    )
    pattern_analysis: Optional[PatternAnalysis] = pydantic.Field(
        default=None,
        description="Pattern analysis, including pattern matching and pattern recognition",
    )


class AnalysisResult(pydantic.BaseModel):
    status: AnalysisStatus
    details: Union[Analysis, str]
