// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.23;

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

/// @title  AereThresholdAccountFactory
/// @notice Deterministic (CREATE2) factory for non-custodial post-quantum threshold accounts.
///         The account address is a pure function of (entryPoint, scheme, threshold, pubKeys,
///         salt), so it can be counterfactually funded before deployment. Permissionless; the
///         factory holds no authority over any account it deploys.
contract AereThresholdAccountFactory {
    event AccountCreated(address indexed account, uint8 scheme, uint8 threshold, uint8 size, bytes32 salt);

    /// @notice Deploy (or return the existing) threshold account for the given committee + salt.
    function createAccount(
        address entryPoint,
        uint8 scheme,
        uint8 threshold,
        bytes[] calldata pubKeys,
        bytes32 salt
    ) external returns (address account) {
        bytes32 create2Salt = _salt(entryPoint, scheme, threshold, pubKeys, salt);
        address predicted = _addressFor(create2Salt);
        if (predicted.code.length > 0) {
            return predicted; // idempotent
        }
        AereThresholdAccount acc = new AereThresholdAccount{salt: create2Salt}();
        acc.initialize(entryPoint, scheme, threshold, pubKeys);
        account = address(acc);
        require(account == predicted, "addr mismatch");
        emit AccountCreated(account, scheme, threshold, uint8(pubKeys.length), salt);
    }

    /// @notice The counterfactual address of the account for the given committee + salt.
    function computeAddress(
        address entryPoint,
        uint8 scheme,
        uint8 threshold,
        bytes[] calldata pubKeys,
        bytes32 salt
    ) external view returns (address) {
        return _addressFor(_salt(entryPoint, scheme, threshold, pubKeys, salt));
    }

    function _salt(
        address entryPoint,
        uint8 scheme,
        uint8 threshold,
        bytes[] calldata pubKeys,
        bytes32 salt
    ) internal pure returns (bytes32) {
        return keccak256(abi.encode(entryPoint, scheme, threshold, keccak256(abi.encode(pubKeys)), salt));
    }

    function _addressFor(bytes32 create2Salt) internal view returns (address) {
        bytes32 h = keccak256(
            abi.encodePacked(bytes1(0xff), address(this), create2Salt, keccak256(type(AereThresholdAccount).creationCode))
        );
        return address(uint160(uint256(h)));
    }
}
