const DOC_URL: &str = "https://github.com/trailofbits/circomspect/blob/main/doc/analysis_passes.md";

#[derive(Copy, Clone)]
pub enum ReportCode {
    AssertWrongType,
    ParseFail,
    CompilerVersionError,
    WrongTypesInAssignOperation,
    WrongNumberOfArguments(usize, usize),
    UndefinedFunction,
    UndefinedTemplate,
    UninitializedSymbolInExpression,
    UnableToTypeFunction,
    UnreachableConstraints,
    UnknownIndex,
    UnknownDimension,
    SameFunctionDeclaredTwice,
    SameTemplateDeclaredTwice,
    SameSymbolDeclaredTwice,
    StaticInfoWasOverwritten,
    SignalInLineInitialization,
    SignalOutsideOriginalScope,
    FunctionWrongNumberOfArguments,
    FunctionInconsistentTyping,
    FunctionPathWithoutReturn,
    FunctionReturnError,
    ForbiddenDeclarationInFunction,
    NonHomogeneousArray,
    NonBooleanCondition,
    NonCompatibleBranchTypes,
    NonEqualTypesInExpression,
    NonExistentSymbol,
    NoMainFoundInProject,
    NoCompilerVersionWarning,
    MultipleMainInComponent,
    TemplateCallAsArgument,
    TemplateWrongNumberOfArguments,
    TemplateWithReturnStatement,
    TypeCantBeUseAsCondition,
    EmptyArrayInlineDeclaration,
    PrefixOperatorWithWrongTypes,
    InfixOperatorWithWrongTypes,
    InvalidArgumentInCall,
    InconsistentReturnTypesInBlock,
    InconsistentStaticInformation,
    InvalidArrayAccess,
    InvalidSignalAccess,
    InvalidArraySize,
    InvalidArrayType,
    ForStatementIllConstructed,
    BadArrayAccess,
    AssigningAComponentTwice,
    AssigningASignalTwice,
    NotAllowedOperation,
    ConstraintGeneratorInFunction,
    WrongSignalTags,
    InvalidPartialArray,
    MustBeSingleArithmetic,
    ExpectedDimDiffGotDim(usize, usize),
    RuntimeError,
    UnknownTemplate,
    NonQuadratic,
    NonConstantArrayLength,
    NonComputableExpression,
    AnonymousComponentError,
    TupleError,
    // Constraint analysis codes
    UnconstrainedSignal,
    OneConstraintIntermediate,
    NoOutputInInstance,
    ErrorWat2Wasm,
    // Circomspect specific codes
    ShadowingVariable,
    ParameterNameCollision,
    FieldElementComparison,
    FieldElementArithmetic,
    SignalAssignmentStatement,
    UnnecessarySignalAssignment,
    UnusedVariableValue,
    UnusedParameterValue,
    VariableWithoutSideEffect,
    ConstantBranchCondition,
    NonStrictBinaryConversion,
    CyclomaticComplexity,
    TooManyArguments,
    UnconstrainedLessThan,
    UnconstrainedDivision,
    Bn254SpecificCircuit,
    UnderConstrainedSignal,
    UnusedOutputSignal,
}

impl ReportCode {
    pub fn id(&self) -> String {
        use self::ReportCode::*;
        match self {
            ParseFail => "P1000",
            NoMainFoundInProject => "P1001",
            MultipleMainInComponent => "P1002",
            CompilerVersionError => "P1003",
            NoCompilerVersionWarning => "P1004",
            WrongTypesInAssignOperation => "T2000",
            UndefinedFunction => "T2001",
            UndefinedTemplate => "T2002",
            UninitializedSymbolInExpression => "T2003",
            UnableToTypeFunction => "T2004",
            UnreachableConstraints => "T2005",
            SameFunctionDeclaredTwice => "T2006",
            SameTemplateDeclaredTwice => "T2007",
            SameSymbolDeclaredTwice => "T2008",
            StaticInfoWasOverwritten => "T2009",
            SignalInLineInitialization => "T2010",
            SignalOutsideOriginalScope => "T2011",
            FunctionWrongNumberOfArguments => "T2012",
            FunctionInconsistentTyping => "T2013",
            FunctionPathWithoutReturn => "T2014",
            FunctionReturnError => "T2015",
            ForbiddenDeclarationInFunction => "T2016",
            NonHomogeneousArray => "T2017",
            NonBooleanCondition => "T2018",
            NonCompatibleBranchTypes => "T2019",
            NonEqualTypesInExpression => "T2020",
            NonExistentSymbol => "T2021",
            TemplateCallAsArgument => "T2022",
            TemplateWrongNumberOfArguments => "T2023",
            TemplateWithReturnStatement => "T2024",
            TypeCantBeUseAsCondition => "T2025",
            EmptyArrayInlineDeclaration => "T2026",
            PrefixOperatorWithWrongTypes => "T2027",
            InfixOperatorWithWrongTypes => "T2028",
            InvalidArgumentInCall => "T2029",
            InconsistentReturnTypesInBlock => "T2030",
            InconsistentStaticInformation => "T2031",
            InvalidArrayAccess => "T2032",
            InvalidSignalAccess => "T2046",
            InvalidArraySize => "T2033",
            InvalidArrayType => "T2034",
            ForStatementIllConstructed => "T2035",
            BadArrayAccess => "T2035",
            AssigningAComponentTwice => "T2036",
            AssigningASignalTwice => "T2037",
            NotAllowedOperation => "T2038",
            ConstraintGeneratorInFunction => "T2039",
            WrongSignalTags => "T2040",
            AssertWrongType => "T2041",
            UnknownIndex => "T2042",
            InvalidPartialArray => "T2043",
            MustBeSingleArithmetic => "T2044",
            ExpectedDimDiffGotDim(..) => "T2045",
            RuntimeError => "T3001",
            UnknownDimension => "T20460",
            UnknownTemplate => "T20461",
            NonQuadratic => "T20462",
            NonConstantArrayLength => "T20463",
            NonComputableExpression => "T20464",
            WrongNumberOfArguments(..) => "T20465",
            AnonymousComponentError => "TAC01",
            TupleError => "TAC02",
            // Constraint analysis codes
            UnconstrainedSignal => "CA01",
            OneConstraintIntermediate => "CA02",
            NoOutputInInstance => "CA03",
            ErrorWat2Wasm => "W01",
            // Circomspect specific codes
            ShadowingVariable => "CS0001",
            ParameterNameCollision => "CS0002",
            FieldElementComparison => "CS0003",
            FieldElementArithmetic => "CS0004",
            SignalAssignmentStatement => "CS0005",
            UnusedVariableValue => "CS0006",
            UnusedParameterValue => "CS0007",
            VariableWithoutSideEffect => "CS0008",
            ConstantBranchCondition => "CS0009",
            NonStrictBinaryConversion => "CS0010",
            CyclomaticComplexity => "CS0011",
            TooManyArguments => "CS0012",
            UnnecessarySignalAssignment => "CS0013",
            UnconstrainedLessThan => "CS0014",
            UnconstrainedDivision => "CS0015",
            Bn254SpecificCircuit => "CS0016",
            UnderConstrainedSignal => "CS0017",
            UnusedOutputSignal => "CS0018",
        }
        .to_string()
    }

    pub fn name(&self) -> String {
        use self::ReportCode::*;
        match self {
            AssertWrongType => "assert-wrong-type",
            ParseFail => "parse-fail",
            CompilerVersionError => "compiler-version-error",
            WrongTypesInAssignOperation => "wrong-types-in-assign-operation",
            WrongNumberOfArguments(..) => "wrong-number-of-arguments",
            AnonymousComponentError => "anonymous-component-error",
            TupleError => "tuple-error",
            UndefinedFunction => "undefined-function",
            UndefinedTemplate => "undefined-template",
            UninitializedSymbolInExpression => "uninitialized-symbol-in-expression",
            UnableToTypeFunction => "unable-to-type-function",
            UnreachableConstraints => "unreachable-constraints",
            UnknownIndex => "unknown-index",
            UnknownDimension => "unknown-dimension",
            SameFunctionDeclaredTwice => "same-function-declared-twice",
            SameTemplateDeclaredTwice => "same-template-declared-twice",
            SameSymbolDeclaredTwice => "same-symbol-declared-twice",
            StaticInfoWasOverwritten => "static-info-was-overwritten",
            SignalInLineInitialization => "signal-in-line-initialization",
            SignalOutsideOriginalScope => "signal-outside-original-scope",
            FunctionWrongNumberOfArguments => "function-wrong-number-of-arguments",
            FunctionInconsistentTyping => "function-inconsistent-typing",
            FunctionPathWithoutReturn => "function-path-without-return",
            FunctionReturnError => "function-return-error",
            ForbiddenDeclarationInFunction => "forbidden-declaration-in-function",
            NonHomogeneousArray => "non-homogeneous-array",
            NonBooleanCondition => "non-boolean-condition",
            NonCompatibleBranchTypes => "non-compatible-branch-types",
            NonEqualTypesInExpression => "non-equal-types-in-expression",
            NonExistentSymbol => "non-existent-symbol",
            NoMainFoundInProject => "no-main-found-in-project",
            NoCompilerVersionWarning => "no-compiler-version-warning",
            MultipleMainInComponent => "multiple-main-in-component",
            TemplateCallAsArgument => "template-call-as-argument",
            TemplateWrongNumberOfArguments => "template-wrong-number-of-arguments",
            TemplateWithReturnStatement => "template-with-return-statement",
            TypeCantBeUseAsCondition => "type-cant-be-use-as-condition",
            EmptyArrayInlineDeclaration => "empty-array-inline-declaration",
            PrefixOperatorWithWrongTypes => "prefix-operator-with-wrong-types",
            InfixOperatorWithWrongTypes => "infix-operator-with-wrong-types",
            InvalidArgumentInCall => "invalid-argument-in-call",
            InconsistentReturnTypesInBlock => "inconsistent-return-types-in-block",
            InconsistentStaticInformation => "inconsistent-static-information",
            InvalidArrayAccess => "invalid-array-access",
            InvalidSignalAccess => "invalid-signal-access",
            InvalidArraySize => "invalid-array-size",
            InvalidArrayType => "invalid-array-type",
            ForStatementIllConstructed => "for-statement-ill-constructed",
            BadArrayAccess => "bad-array-access",
            AssigningAComponentTwice => "assigning-a-component-twice",
            AssigningASignalTwice => "assigning-a-signal-twice",
            NotAllowedOperation => "not-allowed-operation",
            ConstraintGeneratorInFunction => "constraint-generator-in-function",
            WrongSignalTags => "wrong-signal-tags",
            InvalidPartialArray => "invalid-partial-array",
            MustBeSingleArithmetic => "must-be-single-arithmetic",
            ExpectedDimDiffGotDim(..) => "expected-dim-diff-got-dim",
            RuntimeError => "runtime-error",
            UnknownTemplate => "unknown-template",
            NonQuadratic => "non-quadratic",
            NonConstantArrayLength => "non-constant-array-length",
            NonComputableExpression => "non-computable-expression",
            UnconstrainedSignal => "unconstrained-signal",
            OneConstraintIntermediate => "one-constraint-intermediate",
            NoOutputInInstance => "no-output-in-instance",
            ErrorWat2Wasm => "error-wat2-wasm",
            ShadowingVariable => "shadowing-variable",
            ParameterNameCollision => "parameter-name-collision",
            FieldElementComparison => "field-element-comparison",
            FieldElementArithmetic => "field-element-arithmetic",
            SignalAssignmentStatement => "signal-assignment-statement",
            UnnecessarySignalAssignment => "unnecessary-signal-assignment",
            UnusedVariableValue => "unused-variable-value",
            UnusedParameterValue => "unused-parameter-value",
            VariableWithoutSideEffect => "variable-without-side-effect",
            ConstantBranchCondition => "constant-branch-condition",
            NonStrictBinaryConversion => "non-strict-binary-conversion",
            CyclomaticComplexity => "cyclomatic-complexity",
            TooManyArguments => "too-many-arguments",
            UnconstrainedLessThan => "unconstrained-less-than",
            UnconstrainedDivision => "unconstrained-division",
            Bn254SpecificCircuit => "bn254-specific-circuit",
            UnderConstrainedSignal => "under-constrained-signal",
            UnusedOutputSignal => "unused-output-signal",
        }
        .to_string()
    }

    pub fn url(&self) -> Option<String> {
        use ReportCode::*;
        match self {
            ShadowingVariable => Some("shadowing-variable"),
            FieldElementComparison => Some("field-element-comparison"),
            FieldElementArithmetic => Some("field-element-arithmetic"),
            SignalAssignmentStatement => Some("signal-assignment"),
            UnusedVariableValue => Some("unused-variable-or-parameter"),
            UnusedParameterValue => Some("unused-variable-or-parameter"),
            VariableWithoutSideEffect => Some("side-effect-free-assignment"),
            ConstantBranchCondition => Some("constant-branch-condition"),
            NonStrictBinaryConversion => Some("non-strict-binary-conversion"),
            CyclomaticComplexity => Some("overly-complex-function-or-template"),
            TooManyArguments => Some("overly-complex-function-or-template"),
            UnnecessarySignalAssignment => Some("unnecessary-signal-assignment"),
            UnconstrainedLessThan => Some("unconstrained-less-than"),
            UnconstrainedDivision => Some("unconstrained-division"),
            Bn254SpecificCircuit => Some("bn254-specific-circuit"),
            UnderConstrainedSignal => Some("under-constrained-signal"),
            UnusedOutputSignal => Some("unused-output-signal"),
            // We only provide a URL for Circomspect specific issues.
            _ => None,
        }
        .map(|section| format!("{DOC_URL}#{section}"))
    }
}
