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

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

/// @title AerePasskeyAccountFactoryV2 — CREATE2 factory for V2 passkey accounts.
/// @notice Upgrade over V1:
///         - Deploys V2 accounts (MultiOwnable + ERC-4337 + EIP-1271).
///         - Accepts a list of initial owners (≥1 — at least one passkey to bootstrap).
///         - Address derivation includes the initial owner set + salt — same passkey
///           with different initial-owner-set produces a different counterfactual address,
///           preventing pre-deploy front-running of multi-owner accounts.
contract AerePasskeyAccountFactoryV2 {
    bytes32 public constant ACCOUNT_INIT_CODE_HASH = keccak256(type(AerePasskeyAccountV2).creationCode);

    /// @notice The default EntryPoint that newly created accounts trust.
    ///         Hardcoded at factory deployment time so all accounts share the same EntryPoint.
    address public immutable defaultEntryPoint;

    event AccountCreated(address indexed account, address indexed entryPoint, bytes[] owners, uint256 salt);

    /// @notice Deploying an account with an empty owner set would brick it: no
    ///         key could ever authorize a call, so any funds at its
    ///         counterfactual address would be permanently locked.
    error NoOwners();

    constructor(address _defaultEntryPoint) {
        require(_defaultEntryPoint != address(0), "factory: entryPoint=0");
        defaultEntryPoint = _defaultEntryPoint;
    }

    /// @notice Deterministic address from (initialOwners, salt). Same inputs → same address.
    function predictAddress(bytes[] calldata initialOwners, uint256 salt) public view returns (address) {
        bytes32 saltMix = _saltMix(initialOwners, salt);
        return address(uint160(uint256(keccak256(abi.encodePacked(
            bytes1(0xff), address(this), saltMix, ACCOUNT_INIT_CODE_HASH
        )))));
    }

    /// @notice Idempotent: if already deployed, returns existing; else deploys + initialises.
    function createAccount(bytes[] calldata initialOwners, uint256 salt)
        external returns (AerePasskeyAccountV2 account)
    {
        // An owner-less account is a permanent brick (see NoOwners). The factory
        // NatSpec promises >= 1 initial owner; enforce it before doing anything.
        if (initialOwners.length == 0) revert NoOwners();
        address predicted = predictAddress(initialOwners, salt);
        if (predicted.code.length > 0) {
            return AerePasskeyAccountV2(payable(predicted));
        }
        bytes32 saltMix = _saltMix(initialOwners, salt);
        account = new AerePasskeyAccountV2{salt: saltMix}();
        account.initialize(defaultEntryPoint, initialOwners);
        require(address(account) == predicted, "factory: address mismatch");
        emit AccountCreated(address(account), defaultEntryPoint, initialOwners, salt);
    }

    function _saltMix(bytes[] calldata initialOwners, uint256 salt) internal pure returns (bytes32) {
        return keccak256(abi.encode(initialOwners, salt));
    }
}
