// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AerePasskeyAccount} from "./AerePasskeyAccount.sol";

/// @title AerePasskeyAccountFactory — CREATE2 deployer for passkey-controlled accounts
/// @notice One factory per chain. Given a P-256 public key + salt, deterministically
///         computes (and on first call, deploys) the AerePasskeyAccount address.
///
///         The salt allows the same passkey to control multiple AERE addresses
///         (e.g., personal/business/savings) without provisioning multiple passkeys.
contract AerePasskeyAccountFactory {
    /// @notice Reference implementation bytecode hash — for offchain getAddress() helpers.
    bytes32 public constant ACCOUNT_INIT_CODE_HASH = keccak256(type(AerePasskeyAccount).creationCode);

    event AccountCreated(address indexed account, uint256 indexed pubkeyX, uint256 indexed pubkeyY, uint256 salt);

    /// @notice Returns the deterministic address of the account for (pubkey, salt),
    ///         whether or not it's been deployed yet.
    function predictAddress(uint256 pubkeyX, uint256 pubkeyY, uint256 salt) public view returns (address) {
        bytes32 saltMix = keccak256(abi.encode(pubkeyX, pubkeyY, salt));
        return address(uint160(uint256(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            saltMix,
            ACCOUNT_INIT_CODE_HASH
        )))));
    }

    /// @notice Deploys the AerePasskeyAccount for (pubkey, salt) if not yet deployed,
    ///         then initializes it with the passkey. Idempotent — if already deployed,
    ///         returns the existing address without reverting.
    function createAccount(uint256 pubkeyX, uint256 pubkeyY, uint256 salt) external returns (AerePasskeyAccount account) {
        address predicted = predictAddress(pubkeyX, pubkeyY, salt);
        if (predicted.code.length > 0) {
            // Already deployed (idempotent).
            return AerePasskeyAccount(payable(predicted));
        }
        bytes32 saltMix = keccak256(abi.encode(pubkeyX, pubkeyY, salt));
        account = new AerePasskeyAccount{salt: saltMix}();
        account.initialize(pubkeyX, pubkeyY);
        require(address(account) == predicted, "factory: address mismatch");
        emit AccountCreated(address(account), pubkeyX, pubkeyY, salt);
    }
}
