import type { ContractMethod, ContractProfile } from './contracts'
import { callSimpleReadMethod } from './contracts'
import { getGruCatalogPosture } from './gruCatalog'

const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'

export interface GruStandardStatus {
  id: string
  required: boolean
  detected: boolean
}

export interface GruMetadataField {
  label: string
  value: string
}

export interface GruStandardsProfile {
  isGruSurface: boolean
  wrappedTransport: boolean
  forwardCanonical: boolean | null
  legacyAliasSupport: boolean
  x402Ready: boolean
  minimumUpgradeNoticePeriodSeconds: number | null
  activeVersion?: string
  forwardVersion?: string
  profileId: string
  standards: GruStandardStatus[]
  metadata: GruMetadataField[]
}

const GRU_PROFILE_ID = 'gru-c-star-v2-transport-and-payment'

const STANDARD_DEFINITIONS = [
  { id: 'ERC-20', required: true },
  { id: 'AccessControl', required: true },
  { id: 'Pausable', required: true },
  { id: 'EIP-712', required: true },
  { id: 'ERC-2612', required: true },
  { id: 'ERC-3009', required: true },
  { id: 'ERC-5267', required: true },
  { id: 'IeMoneyToken', required: true },
  { id: 'DeterministicStorageNamespace', required: true },
  { id: 'JurisdictionAndSupervisionMetadata', required: true },
] as const

function method(signature: string, outputType: string, inputTypes: string[] = []): ContractMethod {
  const name = signature.split('(')[0]
  return {
    name,
    signature,
    stateMutability: 'view',
    inputs: inputTypes.map((type, index) => ({ name: `arg${index + 1}`, type })),
    outputs: [{ name: '', type: outputType }],
  }
}

function hasMethod(profile: ContractProfile | null | undefined, name: string): boolean {
  const allMethods = [...(profile?.read_methods || []), ...(profile?.write_methods || [])]
  return allMethods.some((entry) => entry.name === name)
}

async function readOptional(address: string, contractMethod: ContractMethod, values: string[] = []): Promise<string | null> {
  try {
    const value = await callSimpleReadMethod(address, contractMethod, values)
    return value
  } catch {
    return null
  }
}

function looksLikeGruToken(symbol?: string | null, tags?: string[] | null): boolean {
  const normalizedSymbol = (symbol || '').toUpperCase()
  if (normalizedSymbol.startsWith('CW') || normalizedSymbol.startsWith('C')) return true
  const normalizedTags = (tags || []).map((tag) => tag.toLowerCase())
  return normalizedTags.includes('compliant') || normalizedTags.includes('wrapped') || normalizedTags.includes('bridge')
}

function parseOptionalNumber(value: string | null): number | null {
  if (!value || !/^\d+$/.test(value)) return null
  const parsed = Number(value)
  return Number.isFinite(parsed) ? parsed : null
}

export async function getGruStandardsProfileSafe(input: {
  address: string
  symbol?: string | null
  tags?: string[] | null
  contractProfile?: ContractProfile | null
}): Promise<{ ok: boolean; data: GruStandardsProfile | null }> {
  const { address, symbol, tags, contractProfile } = input

  const [
    currencyCode,
    versionTag,
    assetId,
    assetVersionId,
    governanceProfileId,
    supervisionProfileId,
    storageNamespace,
    primaryJurisdiction,
    regulatoryDisclosureURI,
    reportingURI,
    minimumUpgradeNoticePeriod,
    wrappedTransport,
    forwardCanonical,
    paused,
    domainSeparator,
    nonces,
    authorizationState,
    defaultAdminRole,
  ] = await Promise.all([
    readOptional(address, method('currencyCode()(string)', 'string')),
    readOptional(address, method('versionTag()(string)', 'string')),
    readOptional(address, method('assetId()(bytes32)', 'bytes32')),
    readOptional(address, method('assetVersionId()(bytes32)', 'bytes32')),
    readOptional(address, method('governanceProfileId()(bytes32)', 'bytes32')),
    readOptional(address, method('supervisionProfileId()(bytes32)', 'bytes32')),
    readOptional(address, method('storageNamespace()(bytes32)', 'bytes32')),
    readOptional(address, method('primaryJurisdiction()(string)', 'string')),
    readOptional(address, method('regulatoryDisclosureURI()(string)', 'string')),
    readOptional(address, method('reportingURI()(string)', 'string')),
    readOptional(address, method('minimumUpgradeNoticePeriod()(uint256)', 'uint256')),
    readOptional(address, method('wrappedTransport()(bool)', 'bool')),
    readOptional(address, method('forwardCanonical()(bool)', 'bool')),
    readOptional(address, method('paused()(bool)', 'bool')),
    readOptional(address, method('DOMAIN_SEPARATOR()(bytes32)', 'bytes32')),
    readOptional(address, method('nonces(address)(uint256)', 'uint256', ['address']), [ZERO_ADDRESS]),
    readOptional(address, method('authorizationState(address,bytes32)(bool)', 'bool', ['address', 'bytes32']), [ZERO_ADDRESS, ZERO_BYTES32]),
    readOptional(address, method('DEFAULT_ADMIN_ROLE()(bytes32)', 'bytes32')),
  ])

  const hasErc20Shape =
    Boolean(symbol) ||
    hasMethod(contractProfile, 'name') ||
    hasMethod(contractProfile, 'symbol') ||
    hasMethod(contractProfile, 'decimals') ||
    hasMethod(contractProfile, 'totalSupply')

  const detectedMap: Record<string, boolean> = {
    'ERC-20': hasErc20Shape,
    AccessControl: defaultAdminRole != null || hasMethod(contractProfile, 'grantRole') || hasMethod(contractProfile, 'hasRole'),
    Pausable: paused != null || hasMethod(contractProfile, 'paused'),
    'EIP-712': domainSeparator != null || hasMethod(contractProfile, 'DOMAIN_SEPARATOR'),
    'ERC-2612': nonces != null || hasMethod(contractProfile, 'permit') || hasMethod(contractProfile, 'nonces'),
    'ERC-3009': authorizationState != null || hasMethod(contractProfile, 'authorizationState'),
    'ERC-5267': hasMethod(contractProfile, 'eip712Domain'),
    IeMoneyToken: currencyCode != null || versionTag != null,
    DeterministicStorageNamespace: storageNamespace != null,
    JurisdictionAndSupervisionMetadata:
      governanceProfileId != null ||
      supervisionProfileId != null ||
      primaryJurisdiction != null ||
      regulatoryDisclosureURI != null ||
      reportingURI != null ||
      minimumUpgradeNoticePeriod != null ||
      wrappedTransport != null,
  }

  const isGruSurface =
    looksLikeGruToken(symbol, tags) ||
    Boolean(currencyCode) ||
    Boolean(versionTag) ||
    Boolean(assetId) ||
    Boolean(governanceProfileId) ||
    Boolean(storageNamespace)

  if (!isGruSurface) {
    return { ok: true, data: null }
  }

  const x402Ready = Boolean(detectedMap['EIP-712'] && detectedMap['ERC-5267'] && (detectedMap['ERC-2612'] || detectedMap['ERC-3009']))
  const minimumUpgradeNoticePeriodSeconds = parseOptionalNumber(minimumUpgradeNoticePeriod)
  const legacyAliasSupport = hasMethod(contractProfile, 'legacyAliases')
  const catalogPosture = getGruCatalogPosture({ symbol, address, tags })

  const metadata: GruMetadataField[] = [
    currencyCode ? { label: 'Currency Code', value: currencyCode } : null,
    versionTag ? { label: 'Version Tag', value: versionTag } : null,
    assetId ? { label: 'Asset ID', value: assetId } : null,
    assetVersionId ? { label: 'Asset Version ID', value: assetVersionId } : null,
    governanceProfileId ? { label: 'Governance Profile', value: governanceProfileId } : null,
    supervisionProfileId ? { label: 'Supervision Profile', value: supervisionProfileId } : null,
    storageNamespace ? { label: 'Storage Namespace', value: storageNamespace } : null,
    primaryJurisdiction ? { label: 'Primary Jurisdiction', value: primaryJurisdiction } : null,
    regulatoryDisclosureURI ? { label: 'Disclosure URI', value: regulatoryDisclosureURI } : null,
    reportingURI ? { label: 'Reporting URI', value: reportingURI } : null,
    minimumUpgradeNoticePeriod ? { label: 'Upgrade Notice Period', value: `${minimumUpgradeNoticePeriod} seconds` } : null,
    wrappedTransport != null ? { label: 'Wrapped Transport', value: wrappedTransport } : null,
    forwardCanonical != null ? { label: 'Forward Canonical', value: forwardCanonical } : null,
    legacyAliasSupport ? { label: 'Legacy Alias Support', value: 'true' } : null,
    { label: 'x402 Readiness', value: x402Ready ? 'true' : 'false' },
  ].filter(Boolean) as GruMetadataField[]

  return {
    ok: true,
    data: {
      isGruSurface: true,
      wrappedTransport: wrappedTransport === 'true',
      forwardCanonical: forwardCanonical === 'true' ? true : forwardCanonical === 'false' ? false : null,
      legacyAliasSupport,
      x402Ready,
      minimumUpgradeNoticePeriodSeconds,
      activeVersion: catalogPosture?.activeVersion,
      forwardVersion: catalogPosture?.forwardVersion,
      profileId: GRU_PROFILE_ID,
      standards: STANDARD_DEFINITIONS.map((entry) => ({
        id: entry.id,
        required: entry.required,
        detected: Boolean(detectedMap[entry.id]),
      })),
      metadata,
    },
  }
}
