import type { ProviderType } from "./providers";

export const REQUIREMENT_STATUS = {
  PASS: "PASS",
  FAIL: "FAIL",
  MANUAL: "MANUAL",
  NO_FINDINGS: "No findings",
} as const;

export type RequirementStatus =
  (typeof REQUIREMENT_STATUS)[keyof typeof REQUIREMENT_STATUS];

export const COMPLIANCE_OVERVIEW_TYPE = {
  OVERVIEW: "compliance-overviews",
  REQUIREMENTS_STATUS: "compliance-requirements-status",
} as const;

export type ComplianceOverviewType =
  (typeof COMPLIANCE_OVERVIEW_TYPE)[keyof typeof COMPLIANCE_OVERVIEW_TYPE];

export interface CompliancesOverview {
  data: ComplianceOverviewData[];
}

export interface ComplianceOverviewData {
  type: ComplianceOverviewType;
  id: string;
  attributes: {
    framework: string;
    version: string;
    requirements_passed: number;
    requirements_failed: number;
    requirements_manual: number;
    total_requirements: number;
  };
}

export interface Requirement {
  name: string;
  description: string;
  status: RequirementStatus;
  pass: number;
  fail: number;
  manual: number;
  check_ids: string[];
  // True when the FAIL is caused solely by an invalid scan config.
  invalid_config?: boolean;
  // This is to allow any key to be added to the requirement object
  // because each compliance has different keys
  [key: string]: string | string[] | number | boolean | object[] | undefined;
}

/** Check id → provider types it belongs to, for provider-labeled check
 *  lists in the cross-provider view (a check can exist in several). */
export type CheckProviderTypesMap = Partial<Record<string, ProviderType[]>>;

export interface Control {
  label: string;
  pass: number;
  fail: number;
  manual: number;
  requirements: Requirement[];
}

export interface Category {
  name: string;
  pass: number;
  fail: number;
  manual: number;
  controls: Control[];
  percentualScore?: number;
}

export interface Framework {
  name: string;
  pass: number;
  fail: number;
  manual: number;
  categories: Category[];
  // Optional: flat structure for frameworks like MITRE that don't have categories
  requirements?: Requirement[];
}

export interface FailedSection {
  name: string;
  total: number;
  types?: Record<string, number>;
}

export const TOP_FAILED_DATA_TYPE = {
  SECTIONS: "sections",
  REQUIREMENTS: "requirements",
} as const;

export type TopFailedDataType =
  (typeof TOP_FAILED_DATA_TYPE)[keyof typeof TOP_FAILED_DATA_TYPE];

export interface TopFailedResult {
  items: FailedSection[];
  type: TopFailedDataType;
  // True when items already cover every relevant category (zero-fill). The
  // chart should render the supplied list as-is instead of falling back to
  // severity placeholders when totals are zero.
  prepopulated?: boolean;
}

export interface RequirementsTotals {
  pass: number;
  fail: number;
  manual: number;
}

// API Responses types:
export interface ENSAttributesMetadata {
  IdGrupoControl: string;
  Marco: string;
  Categoria: string;
  DescripcionControl: string;
  Tipo: string;
  Nivel: string;
  Dimensiones: string[];
  ModoEjecucion: string;
  Dependencias: unknown[];
}

export interface ISO27001AttributesMetadata {
  Category: string;
  Objetive_ID: string;
  Objetive_Name: string;
  Check_Summary: string;
}

export interface CISAttributesMetadata {
  Section: string;
  SubSection: string | null;
  Profile: string; // "Level 1"/"Level 2" (M365 prefixes the tier: "E3 Level 1", "E5 Level 2")
  AssessmentStatus: string; // "Manual" or "Automated"
  Description: string;
  RationaleStatement: string;
  ImpactStatement: string;
  RemediationProcedure: string;
  AuditProcedure: string;
  AdditionalInformation: string;
  DefaultValue: string | null;
  References: string;
}

export interface AWSWellArchitectedAttributesMetadata {
  Name: string;
  WellArchitectedQuestionId: string;
  WellArchitectedPracticeId: string;
  Section: string;
  SubSection: string;
  LevelOfRisk: string;
  AssessmentMethod: string;
  Description: string;
  ImplementationGuidanceUrl: string;
}

export interface ThreatAttributesMetadata {
  Title: string;
  Section: string;
  SubSection: string;
  AttributeDescription: string;
  AdditionalInformation: string;
  LevelOfRisk: number;
  Weight: number;
}

export interface KISAAttributesMetadata {
  Domain: string;
  Subdomain: string;
  Section: string;
  AuditChecklist: string[];
  RelatedRegulations: string[];
  AuditEvidence: string[];
  NonComplianceCases: string[];
}

export interface C5AttributesMetadata {
  Section: string;
  SubSection: string;
  Type: string;
  AboutCriteria: string;
  ComplementaryCriteria: string;
}

export interface MITREAttributesMetadata {
  // Dynamic cloud service field - could be AWSService, GCPService, AzureService, etc.
  [key: string]: string;
  Category: string; // "Protect", "Detect", "Respond"
  Value: string; // "Minimal", "Partial", "Significant"
  Comment: string;
}

export interface GenericAttributesMetadata {
  ItemId: string;
  Section: string;
  SubSection: string;
  SubGroup: string | null;
  Service: string | null;
  Type: string | null;
}

export interface CSAAttributesMetadata {
  Section: string;
  CCMLite: string;
  IaaS: string;
  PaaS: string;
  SaaS: string;
  ScopeApplicability: Array<{
    ReferenceId: string;
    Identifiers: string[];
  }>;
}

export interface CCCAttributesMetadata {
  FamilyName: string;
  FamilyDescription: string;
  Section: string;
  SubSection: string;
  SubSectionObjective: string;
  Applicability: string[];
  Recommendation: string;
  SectionThreatMappings: Array<{
    ReferenceId: string;
    Identifiers: string[];
  }>;
  SectionGuidelineMappings: Array<{
    ReferenceId: string;
    Identifiers: string[];
  }>;
}

// ASD Essential Eight enums — modelled on the canonical Maturity Model
// (Nov 2023). Only ML1 ships today; ML2/ML3 are scoped out of the framework
// but kept here so the type covers any future expansion without a schema
// edit. AssessmentStatus and CloudApplicability are exhaustive per the JSON
// fixture; new variants must be added explicitly.
export const ASD_MATURITY_LEVEL = {
  ML1: "ML1",
  ML2: "ML2",
  ML3: "ML3",
} as const;
export type ASDMaturityLevel =
  (typeof ASD_MATURITY_LEVEL)[keyof typeof ASD_MATURITY_LEVEL];

export const ASD_ASSESSMENT_STATUS = {
  AUTOMATED: "Automated",
  MANUAL: "Manual",
} as const;
export type ASDAssessmentStatus =
  (typeof ASD_ASSESSMENT_STATUS)[keyof typeof ASD_ASSESSMENT_STATUS];

export const ASD_CLOUD_APPLICABILITY = {
  FULL: "full",
  PARTIAL: "partial",
  LIMITED: "limited",
  NON_APPLICABLE: "non-applicable",
} as const;
export type ASDCloudApplicability =
  (typeof ASD_CLOUD_APPLICABILITY)[keyof typeof ASD_CLOUD_APPLICABILITY];

export interface ASDEssentialEightAttributesMetadata {
  Section: string;
  MaturityLevel: ASDMaturityLevel;
  AssessmentStatus: ASDAssessmentStatus;
  CloudApplicability: ASDCloudApplicability;
  MitigatedThreats: string[];
  Description: string;
  RationaleStatement: string;
  ImpactStatement: string;
  RemediationProcedure: string;
  AuditProcedure: string;
  AdditionalInformation: string;
  References: string;
}

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null && !Array.isArray(value);

const isOneOf = <T extends string>(
  values: Record<string, T>,
  value: unknown,
): value is T => (Object.values(values) as T[]).includes(value as T);

const isStringArray = (value: unknown): value is string[] =>
  Array.isArray(value) && value.every((item) => typeof item === "string");

const isOptionalString = (value: unknown): value is string | undefined =>
  value === undefined || typeof value === "string";

const isOptionalStringArray = (value: unknown): value is string[] | undefined =>
  value === undefined || isStringArray(value);

const ASD_METADATA_STRING_FIELDS = [
  "Section",
  "Description",
  "RationaleStatement",
  "ImpactStatement",
  "RemediationProcedure",
  "AuditProcedure",
  "AdditionalInformation",
  "References",
] as const satisfies readonly (keyof ASDEssentialEightAttributesMetadata)[];

export const isASDMaturityLevel = (value: unknown): value is ASDMaturityLevel =>
  isOneOf(ASD_MATURITY_LEVEL, value);

export const isASDAssessmentStatus = (
  value: unknown,
): value is ASDAssessmentStatus => isOneOf(ASD_ASSESSMENT_STATUS, value);

export const isASDCloudApplicability = (
  value: unknown,
): value is ASDCloudApplicability => isOneOf(ASD_CLOUD_APPLICABILITY, value);

export const isASDEssentialEightAttributesMetadata = (
  value: unknown,
): value is ASDEssentialEightAttributesMetadata =>
  isRecord(value) &&
  ASD_METADATA_STRING_FIELDS.every(
    (field) => typeof value[field] === "string",
  ) &&
  isASDMaturityLevel(value.MaturityLevel) &&
  isASDAssessmentStatus(value.AssessmentStatus) &&
  isASDCloudApplicability(value.CloudApplicability) &&
  isStringArray(value.MitigatedThreats);

export interface ASDEssentialEightRequirement extends Requirement {
  maturity_level: ASDEssentialEightAttributesMetadata["MaturityLevel"];
  assessment_status: ASDEssentialEightAttributesMetadata["AssessmentStatus"];
  cloud_applicability: ASDEssentialEightAttributesMetadata["CloudApplicability"];
  mitigated_threats: ASDEssentialEightAttributesMetadata["MitigatedThreats"];
  implementation_notes: ASDEssentialEightAttributesMetadata["Description"];
  rationale_statement: ASDEssentialEightAttributesMetadata["RationaleStatement"];
  impact_statement: ASDEssentialEightAttributesMetadata["ImpactStatement"];
  remediation_procedure: ASDEssentialEightAttributesMetadata["RemediationProcedure"];
  audit_procedure: ASDEssentialEightAttributesMetadata["AuditProcedure"];
  additional_information: ASDEssentialEightAttributesMetadata["AdditionalInformation"];
  references: ASDEssentialEightAttributesMetadata["References"];
}

export interface OktaIDaaSStigAttributesMetadata {
  Section: string;
  Severity: string;
  RuleID: string;
  StigID: string;
  CCI?: string[];
  CheckText?: string;
  FixText?: string;
}

const OKTA_IDAAS_STIG_REQUIRED_STRING_FIELDS = [
  "Section",
  "Severity",
  "RuleID",
  "StigID",
] as const satisfies readonly (keyof OktaIDaaSStigAttributesMetadata)[];

export const isOktaIDaaSStigAttributesMetadata = (
  value: unknown,
): value is OktaIDaaSStigAttributesMetadata =>
  isRecord(value) &&
  OKTA_IDAAS_STIG_REQUIRED_STRING_FIELDS.every(
    (field) => typeof value[field] === "string",
  ) &&
  isOptionalStringArray(value.CCI) &&
  isOptionalString(value.CheckText) &&
  isOptionalString(value.FixText);

export interface OktaIDaaSStigRequirement extends Requirement {
  severity: OktaIDaaSStigAttributesMetadata["Severity"];
  rule_id: OktaIDaaSStigAttributesMetadata["RuleID"];
  stig_id: OktaIDaaSStigAttributesMetadata["StigID"];
  cci: OktaIDaaSStigAttributesMetadata["CCI"];
  check_text: OktaIDaaSStigAttributesMetadata["CheckText"];
  fix_text: OktaIDaaSStigAttributesMetadata["FixText"];
}

// DORA (Digital Operational Resilience Act, Regulation (EU) 2022/2554).
// Universal framework — flat attributes dict with Pillar/Article/ArticleTitle.
// `Pillar` is the canonical grouping key for tables and PDF; the enum mirrors
// the five DORA pillars declared in `prowler/compliance/dora_2022_2554.json`.
export const DORA_PILLAR = {
  ICT_RISK_MANAGEMENT: "ICT Risk Management",
  INCIDENT_REPORTING: "ICT-Related Incident Reporting",
  RESILIENCE_TESTING: "Digital Operational Resilience Testing",
  THIRD_PARTY_RISK: "ICT Third-Party Risk Management",
  INFORMATION_SHARING: "Information Sharing",
} as const;
export type DORAPillar = (typeof DORA_PILLAR)[keyof typeof DORA_PILLAR];

export interface DORAAttributesMetadata {
  Pillar: DORAPillar;
  Article: string;
  ArticleTitle: string;
}

export interface DORARequirement extends Requirement {
  pillar: DORAAttributesMetadata["Pillar"];
  article: DORAAttributesMetadata["Article"];
  article_title: DORAAttributesMetadata["ArticleTitle"];
}

export interface CISControlsAttributesMetadata {
  Section: string;
  Function: string | null;
  AssetType: string | null;
  ImplementationGroups: string[] | null;
}

export interface CISControlsRequirement extends Requirement {
  function?: string;
  asset_type?: string;
  implementation_groups?: string[];
}

export interface AttributesItemData {
  type: "compliance-requirements-attributes";
  id: string;
  attributes: {
    framework_description: string;
    name?: string;
    framework: string;
    version: string;
    description: string;
    attributes: {
      metadata:
        | ENSAttributesMetadata[]
        | ISO27001AttributesMetadata[]
        | CISAttributesMetadata[]
        | AWSWellArchitectedAttributesMetadata[]
        | ThreatAttributesMetadata[]
        | KISAAttributesMetadata[]
        | C5AttributesMetadata[]
        | MITREAttributesMetadata[]
        | CCCAttributesMetadata[]
        | CSAAttributesMetadata[]
        | ASDEssentialEightAttributesMetadata[]
        | OktaIDaaSStigAttributesMetadata[]
        | DORAAttributesMetadata[]
        | CISControlsAttributesMetadata[]
        | GenericAttributesMetadata[];
      check_ids: string[];
      // MITRE structure
      technique_details?: {
        tactics: string[];
        subtechniques: string[];
        platforms: string[];
        technique_url: string;
      };
    };
  };
}

export interface RequirementItemData {
  type: "compliance-requirements-details";
  id: string;
  attributes: {
    framework: string;
    version: string;
    description: string;
    status: RequirementStatus;
    // True when the FAIL is caused solely by an invalid scan config.
    invalid_config?: boolean;
    // For Threat compliance:
    passed_findings?: number;
    total_findings?: number;
  };
}

export interface AttributesData {
  data: AttributesItemData[];
}

export interface RequirementsData {
  data: RequirementItemData[];
}

export interface RegionData {
  name: string;
  failurePercentage: number;
  totalRequirements: number;
  failedRequirements: number;
}

export interface CategoryData {
  name: string;
  failurePercentage: number;
  totalRequirements: number;
  failedRequirements: number;
}
