import { ethers } from 'ethers';
import { deepFreeze } from './freeze';
import type { Bytes32, V3DeploymentName } from './types';

/** Sorted canonical function signatures generated from the reviewed Solidity ABI artifacts. */
const V3_CANONICAL_ABI_SIGNATURES = deepFreeze({
  accountabilityBond: [
    "DOMAIN_SEPARATOR()",
    "FLOURISHING_NON_CONTROL_BOUNDARY()",
    "activate(uint256)",
    "advanceObjectiveEvidence(uint256)",
    "bondOf(uint256)",
    "cancelBeforeActivation(uint256)",
    "consensus()",
    "createBond((uint256,address,address,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint96,uint256,string),(address,bytes32,bytes32,bytes32,uint64,uint64,uint64,uint64,uint256,bytes32,bytes32,bytes32,bytes32,uint8,uint8),(address[],address[]),bytes,bytes)",
    "createBond((uint256,address,address,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint96,uint256,string),bytes,bytes)",
    "creditAssets(address)",
    "creditLiabilities(address)",
    "creditOf(address,address)",
    "disputeRegistry()",
    "enforcementOf(uint256)",
    "enforcementStateOf(uint256)",
    "expireUnactivated(uint256)",
    "expireUnfunded(uint256)",
    "forceExit(uint256)",
    "fundNative(uint256)",
    "hashEnforcementConfig((address,bytes32,bytes32,bytes32,uint64,uint64,uint64,uint64,uint256,bytes32,bytes32,bytes32,bytes32,uint8,uint8))",
    "hashObjectiveEvidence((address,uint256,uint256,bytes32,bytes32,address,bytes32,bytes32,uint64,uint64,uint64,uint96,uint64))",
    "hashTerms((uint256,address,address,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint96,uint256,string))",
    "isCreditSolvent(address)",
    "markExpired(uint256)",
    "maxAggregateNativeLiability()",
    "maxBondNativePrincipal()",
    "nativeLiability()",
    "nextBondId()",
    "nonceUsed(address,uint96)",
    "owner()",
    "pause()",
    "paused()",
    "pilotController()",
    "submitObjectiveEvidence((address,uint256,uint256,bytes32,bytes32,address,bytes32,bytes32,uint64,uint64,uint64,uint96,uint64),address[],bytes[])",
    "unpause()",
    "withdrawCredit(address)",
  ],
  partnershipBond: [
    "DOMAIN_SEPARATOR()",
    "FLOURISHING_MISSION()",
    "FLOURISHING_NON_CONTROL_BOUNDARY()",
    "MUTUAL_SETTLEMENT_TYPEHASH()",
    "PROTOCOL_VERSION_HASH()",
    "SETTLEMENT_OUTCOME()",
    "TERMS_TYPEHASH()",
    "activate(uint256)",
    "advanceToFinalizable(uint256)",
    "authorizedRespondent(uint256,address)",
    "bondOf(uint256)",
    "cancelBeforeActivation(uint256)",
    "cancelStalledSettlement(uint256)",
    "challengeSettlement(uint256,uint256)",
    "consensus()",
    "createBond((uint256,address,address,uint256,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint64,uint96,uint256,string),bytes,bytes)",
    "creditAssets(address)",
    "creditLiabilities(address)",
    "creditOf(address,address)",
    "disputePayoutCap(uint256)",
    "disputePayoutSource(uint256)",
    "disputeRegistry()",
    "disputeSubject(uint256)",
    "expireUnactivated(uint256)",
    "expireUnfunded(uint256)",
    "forceExit(uint256)",
    "freezeSettlement(uint256,uint96,(uint256,uint256,uint256))",
    "fundNative(uint256)",
    "hashMutualSettlement((address,uint256,uint256,bytes32,uint256,uint256,uint96,uint64))",
    "hashTerms((uint256,address,address,uint256,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint64,uint96,uint256,string))",
    "isAuthorizedChallenger(uint256,address)",
    "isCreditSolvent(address)",
    "markExpired(uint256)",
    "maxAggregateNativeLiability()",
    "maxBondNativePrincipal()",
    "maxPartyNativePrincipal()",
    "mutualSettlementNonceUsed(uint256,uint96)",
    "nativeLiability()",
    "nextBondId()",
    "nonceUsed(address,uint96)",
    "owner()",
    "pause()",
    "paused()",
    "pilotController()",
    "releaseDisputePayout(uint256,address,uint256)",
    "requestSettlement(uint256)",
    "resolveChallenge(uint256)",
    "rewardProgram()",
    "settle(uint256)",
    "settleByMutualConsent((address,uint256,uint256,bytes32,uint256,uint256,uint96,uint64),bytes,bytes)",
    "settlementChallengeContext(uint256)",
    "settlementSnapshot(uint256,(uint256,uint256,uint256))",
    "skimSurplus(address,address,uint256)",
    "unpause()",
    "withdrawCredit(address)",
  ],
  governance: [
    "CANONICAL_POLICY_SHA256()",
    "MAX_GUARDIAN_PAUSE_TARGETS()",
    "MAX_GUARDIAN_ROTATION_DELAY()",
    "MAX_PROPOSAL_DATA_LENGTH()",
    "MAX_SIGNERS()",
    "MAX_TIMELOCK_SECONDS()",
    "PROPOSAL_LIFETIME()",
    "acceptGuardian()",
    "addGuardianPauseTarget(address)",
    "callPolicy(address,bytes4)",
    "cancel(uint256)",
    "cancelGuardianRotation()",
    "cancellationConfirmations(uint256)",
    "cancellationConfirmed(uint256,address)",
    "canonicalRuntimeCodehash(address)",
    "confirm(uint256)",
    "confirmed(uint256,address)",
    "constructorPinnedPolicyTarget(address)",
    "execute(uint256)",
    "governanceConfigVersion()",
    "governancePolicyCommitment()",
    "guardian()",
    "guardianPause(address)",
    "guardianPauseTargetCount()",
    "guardianRotationDelay()",
    "isGovernanceCallAllowed(address,uint256,bytes)",
    "isGuardianPauseTarget(address)",
    "isSigner(address)",
    "minimumTimelockSeconds()",
    "pendingGuardian()",
    "pendingGuardianAcceptAfter()",
    "pinCanonicalAddress(address,bytes32)",
    "policyCommitment()",
    "policyVersion()",
    "proposalCount()",
    "proposalPolicyCommitment(uint256)",
    "proposals(uint256)",
    "propose(address,uint256,bytes)",
    "proposeGuardian(address)",
    "setCallPolicy(address,bytes4,bool,uint96,bytes32)",
    "setSigners(address[],uint256)",
    "setTimelock(uint256)",
    "signers(uint256)",
    "threshold()",
    "timelockSeconds()",
  ],
  mandateRegistry: [
    "DOMAIN_SEPARATOR()",
    "EXECUTION_DOMAIN()",
    "GOVERNANCE_POLICY_MODULE_ID()",
    "MANDATE_TYPEHASH()",
    "MAX_ALLOWED_ACTIONS()",
    "MAX_ALLOWED_CONTRACTS()",
    "MAX_ALLOWED_SELECTORS()",
    "MAX_PROHIBITED_ACTIONS()",
    "PROTOCOL_VERSION()",
    "allowedModules(address)",
    "consume((uint256,address,uint256,address,uint256,address,address,bytes32[],bytes32[],address[],bytes4[],address,uint256,uint256,bytes32,bytes32,uint64,uint64,uint96,uint256,string),bytes32,address,bytes4,uint256,address)",
    "consumeExecution(bytes,bytes32,address,bytes4,uint256,address,bytes32,bytes32,bytes32,uint256)",
    "hashMandate((uint256,address,uint256,address,uint256,address,address,bytes32[],bytes32[],address[],bytes4[],address,uint256,uint256,bytes32,bytes32,uint64,uint64,uint96,uint256,string))",
    "identityRegistry()",
    "identityRegistryCodehash()",
    "isActive(bytes32)",
    "mandateForNonce(address,uint96)",
    "owner()",
    "registerMandate((uint256,address,uint256,address,uint256,address,address,bytes32[],bytes32[],address[],bytes4[],address,uint256,uint256,bytes32,bytes32,uint64,uint64,uint96,uint256,string),bytes)",
    "requiredExecutionAdapterCodehash()",
    "revokeMandate(bytes32)",
    "revokeMandate(bytes32,address)",
    "setModuleAuthorization(address,bool)",
    "state(bytes32)",
  ],
} as const);

/** Callable SDK write surface; disabled legacy selectors and admin-only setters are omitted. */
const WRITE_SIGNATURES = deepFreeze({
  accountabilityBond: [
    "activate(uint256)",
    "advanceObjectiveEvidence(uint256)",
    "cancelBeforeActivation(uint256)",
    "createBond((uint256,address,address,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint96,uint256,string),(address,bytes32,bytes32,bytes32,uint64,uint64,uint64,uint64,uint256,bytes32,bytes32,bytes32,bytes32,uint8,uint8),(address[],address[]),bytes,bytes)",
    "expireUnactivated(uint256)",
    "expireUnfunded(uint256)",
    "forceExit(uint256)",
    "fundNative(uint256)",
    "markExpired(uint256)",
    "submitObjectiveEvidence((address,uint256,uint256,bytes32,bytes32,address,bytes32,bytes32,uint64,uint64,uint64,uint96,uint64),address[],bytes[])",
    "withdrawCredit(address)",
  ],
  partnershipBond: [
    "activate(uint256)",
    "cancelBeforeActivation(uint256)",
    "createBond((uint256,address,address,uint256,uint256,address,bytes32,bytes32,uint8,uint16,bytes32,uint256,uint64,uint64,uint64,uint64,uint96,uint256,string),bytes,bytes)",
    "expireUnactivated(uint256)",
    "expireUnfunded(uint256)",
    "forceExit(uint256)",
    "fundNative(uint256)",
    "markExpired(uint256)",
    "settleByMutualConsent((address,uint256,uint256,bytes32,uint256,uint256,uint96,uint64),bytes,bytes)",
    "withdrawCredit(address)",
  ],
  governance: [
    "acceptGuardian()",
    "cancel(uint256)",
    "cancelGuardianRotation()",
    "confirm(uint256)",
    "execute(uint256)",
    "guardianPause(address)",
    "propose(address,uint256,bytes)",
    "proposeGuardian(address)",
  ],
  mandateRegistry: [
    "consumeExecution(bytes,bytes32,address,bytes4,uint256,address,bytes32,bytes32,bytes32,uint256)",
    "registerMandate((uint256,address,uint256,address,uint256,address,address,bytes32[],bytes32[],address[],bytes4[],address,uint256,uint256,bytes32,bytes32,uint64,uint64,uint96,uint256,string),bytes)",
    "revokeMandate(bytes32)",
    "revokeMandate(bytes32,address)",
  ],
} as const);

const hashSignatures = (signatures: readonly string[]): Bytes32 =>
  ethers.keccak256(ethers.toUtf8Bytes(signatures.join('\n'))) as Bytes32;

export const V3_ABI_IDENTITIES = deepFreeze({
  accountabilityBond: hashSignatures(V3_CANONICAL_ABI_SIGNATURES.accountabilityBond),
  partnershipBond: hashSignatures(V3_CANONICAL_ABI_SIGNATURES.partnershipBond),
  governance: hashSignatures(V3_CANONICAL_ABI_SIGNATURES.governance),
  mandateRegistry: hashSignatures(V3_CANONICAL_ABI_SIGNATURES.mandateRegistry),
} satisfies Record<V3DeploymentName, Bytes32>);

export interface V3WriteMethod {
  readonly signature: string;
  readonly selector: `0x${string}`;
  readonly capability: Bytes32;
  readonly payable: boolean;
}
const PAYABLE_SIGNATURES: readonly string[] = [
  'fundNative(uint256)',
];
const writeMethod = (deployment: V3DeploymentName, signature: string): V3WriteMethod => ({
  signature,
  selector: ethers.id(signature).slice(0, 10) as `0x${string}`,
  capability: ethers.id(`Vaultfire.V3.Capability.${deployment}.${signature}`) as Bytes32,
  payable: PAYABLE_SIGNATURES.includes(signature),
});

export const V3_WRITE_METHODS = deepFreeze(Object.fromEntries(
  Object.entries(WRITE_SIGNATURES).map(([deployment, signatures]) => [
    deployment,
    signatures.map((signature) => writeMethod(deployment as V3DeploymentName, signature)),
  ]),
) as unknown as Record<V3DeploymentName, readonly V3WriteMethod[]>);
