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

import "./IVaultfireIdentity.sol";
import "./IVaultfireBonds.sol";
import "./IVaultfireReputation.sol";

/// @title AgentInsurancePool — On-chain AI agent insurance via pooled coverage
/// @author Vaultfire Protocol
/// @notice Agents deposit premiums into a shared insurance pool. If an agent causes
///         verified damage, the pool automatically compensates the affected party.
///         Risk tiers are based on trust scores — higher trust = lower premium.
/// @dev Integrates with ERC-8004 identity, bonds, and reputation registries.
contract AgentInsurancePool {
    // ═══════════════════════════════════════════════════
    // STRUCTS
    // ═══════════════════════════════════════════════════

    struct Policy {
        address agent;              // Insured agent (ERC-8004 registered)
        uint256 premiumPaid;        // Total premium deposited (wei)
        uint256 coverageLimit;      // Max payout per claim (wei)
        uint256 activeSince;        // Timestamp when policy activated
        uint256 expiresAt;          // Timestamp when policy expires
        uint256 riskTier;           // 1 = low risk, 2 = medium, 3 = high
        uint256 claimsPaid;         // Total claims paid out for this agent
        bool active;                // Whether policy is currently active
    }

    struct Claim {
        uint256 policyId;           // Policy being claimed against
        address claimant;           // Party filing the claim (affected party)
        address agent;              // Agent the claim is against
        uint256 amount;             // Requested payout amount (wei)
        uint256 filedAt;            // Timestamp when claim was filed
        uint256 resolvedAt;         // Timestamp when resolved (0 if pending)
        ClaimStatus status;         // Current claim status
        string evidenceURI;         // IPFS/URI to evidence of damage
    }

    enum ClaimStatus { Pending, Approved, Denied, Paid }

    // ═══════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════

    IVaultfireIdentity public immutable identityRegistry;
    IVaultfireBonds public immutable bondsRegistry;
    IVaultfireReputation public immutable reputationRegistry;

    address public owner;
    address public arbiter;         // Authorized claim resolver

    uint256 public totalPoolBalance;
    uint256 public totalPolicies;
    uint256 public totalClaims;
    uint256 public totalClaimsPaid;

    // Risk tier premium multipliers (basis points — 100 = 1%)
    uint256 public constant LOW_RISK_RATE = 100;        // 1% of coverage
    uint256 public constant MEDIUM_RISK_RATE = 250;     // 2.5% of coverage
    uint256 public constant HIGH_RISK_RATE = 500;       // 5% of coverage

    // Reputation thresholds for risk tiers
    uint256 public constant LOW_RISK_MIN_REP = 70;
    uint256 public constant MEDIUM_RISK_MIN_REP = 40;
    // Below 40 = high risk

    uint256 public constant MIN_POLICY_DURATION = 30 days;
    uint256 public constant MAX_POLICY_DURATION = 365 days;
    uint256 public constant MAX_CLAIM_AMOUNT_BPS = 10000; // 100% of coverage

    mapping(uint256 => Policy) public policies;
    mapping(uint256 => Claim) public claims;
    mapping(address => uint256[]) public agentPolicies;
    mapping(address => bool) public hasActivePolicy;

    // ═══════════════════════════════════════════════════
    // EVENTS
    // ═══════════════════════════════════════════════════

    event PolicyCreated(uint256 indexed policyId, address indexed agent, uint256 coverageLimit, uint256 premium, uint256 riskTier);
    event PolicyExpired(uint256 indexed policyId, address indexed agent);
    event ClaimFiled(uint256 indexed claimId, uint256 indexed policyId, address indexed claimant, uint256 amount);
    event ClaimResolved(uint256 indexed claimId, ClaimStatus status, uint256 payout);
    event PremiumDeposited(address indexed agent, uint256 amount);
    event ArbiterUpdated(address indexed oldArbiter, address indexed newArbiter);

    // ═══════════════════════════════════════════════════
    // MODIFIERS
    // ═══════════════════════════════════════════════════

    modifier onlyOwner() {
        require(msg.sender == owner, "AgentInsurancePool: caller is not owner");
        _;
    }

    modifier onlyArbiter() {
        require(msg.sender == arbiter, "AgentInsurancePool: caller is not arbiter");
        _;
    }

    modifier onlyRegisteredAgent(address agent) {
        (string memory uri, bool active, , ) = identityRegistry.getAgent(agent);
        require(bytes(uri).length > 0 && active, "AgentInsurancePool: agent not registered or inactive");
        _;
    }

    // ═══════════════════════════════════════════════════
    // CONSTRUCTOR
    // ═══════════════════════════════════════════════════

    constructor(
        address _identityRegistry,
        address _bondsRegistry,
        address _reputationRegistry
    ) {
        require(_identityRegistry != address(0), "AgentInsurancePool: zero identity address");
        require(_bondsRegistry != address(0), "AgentInsurancePool: zero bonds address");
        require(_reputationRegistry != address(0), "AgentInsurancePool: zero reputation address");

        identityRegistry = IVaultfireIdentity(_identityRegistry);
        bondsRegistry = IVaultfireBonds(_bondsRegistry);
        reputationRegistry = IVaultfireReputation(_reputationRegistry);
        owner = msg.sender;
        arbiter = msg.sender; // Owner is initial arbiter
    }

    // ═══════════════════════════════════════════════════
    // POLICY MANAGEMENT
    // ═══════════════════════════════════════════════════

    /// @notice Create an insurance policy for a registered agent
    /// @param coverageLimit Maximum payout per claim (wei)
    /// @param duration Policy duration in seconds
    function createPolicy(
        uint256 coverageLimit,
        uint256 duration
    ) external payable onlyRegisteredAgent(msg.sender) {
        require(!hasActivePolicy[msg.sender], "AgentInsurancePool: agent already has active policy");
        require(coverageLimit > 0, "AgentInsurancePool: zero coverage");
        require(duration >= MIN_POLICY_DURATION && duration <= MAX_POLICY_DURATION, "AgentInsurancePool: invalid duration");

        uint256 riskTier = _calculateRiskTier(msg.sender);
        uint256 requiredPremium = _calculatePremium(coverageLimit, riskTier, duration);

        require(msg.value >= requiredPremium, "AgentInsurancePool: insufficient premium");

        uint256 policyId = totalPolicies;
        totalPolicies++;

        policies[policyId] = Policy({
            agent: msg.sender,
            premiumPaid: msg.value,
            coverageLimit: coverageLimit,
            activeSince: block.timestamp,
            expiresAt: block.timestamp + duration,
            riskTier: riskTier,
            claimsPaid: 0,
            active: true
        });

        agentPolicies[msg.sender].push(policyId);
        hasActivePolicy[msg.sender] = true;
        totalPoolBalance += msg.value;

        emit PolicyCreated(policyId, msg.sender, coverageLimit, msg.value, riskTier);
        emit PremiumDeposited(msg.sender, msg.value);
    }

    /// @notice Expire a policy that has passed its expiration date
    /// @param policyId The policy to expire
    function expirePolicy(uint256 policyId) external {
        Policy storage policy = policies[policyId];
        require(policy.active, "AgentInsurancePool: policy not active");
        require(block.timestamp >= policy.expiresAt, "AgentInsurancePool: policy not expired");

        policy.active = false;
        hasActivePolicy[policy.agent] = false;

        emit PolicyExpired(policyId, policy.agent);
    }

    // ═══════════════════════════════════════════════════
    // CLAIMS
    // ═══════════════════════════════════════════════════

    /// @notice File a claim against an insured agent
    /// @param policyId Policy to claim against
    /// @param amount Requested payout amount (wei)
    /// @param evidenceURI URI pointing to evidence of damage
    function fileClaim(
        uint256 policyId,
        uint256 amount,
        string calldata evidenceURI
    ) external {
        Policy storage policy = policies[policyId];
        require(policy.active, "AgentInsurancePool: policy not active");
        require(block.timestamp < policy.expiresAt, "AgentInsurancePool: policy expired");
        require(amount > 0 && amount <= policy.coverageLimit, "AgentInsurancePool: invalid claim amount");
        require(policy.claimsPaid + amount <= policy.coverageLimit, "AgentInsurancePool: exceeds coverage limit");
        require(bytes(evidenceURI).length > 0, "AgentInsurancePool: empty evidence");

        uint256 claimId = totalClaims;
        totalClaims++;

        claims[claimId] = Claim({
            policyId: policyId,
            claimant: msg.sender,
            agent: policy.agent,
            amount: amount,
            filedAt: block.timestamp,
            resolvedAt: 0,
            status: ClaimStatus.Pending,
            evidenceURI: evidenceURI
        });

        emit ClaimFiled(claimId, policyId, msg.sender, amount);
    }

    /// @notice Resolve a claim (approve or deny) — arbiter only
    /// @param claimId The claim to resolve
    /// @param approved Whether to approve the claim
    function resolveClaim(uint256 claimId, bool approved) external onlyArbiter {
        Claim storage claim = claims[claimId];
        require(claim.status == ClaimStatus.Pending, "AgentInsurancePool: claim not pending");

        claim.resolvedAt = block.timestamp;

        if (approved) {
            require(totalPoolBalance >= claim.amount, "AgentInsurancePool: insufficient pool balance");

            Policy storage policy = policies[claim.policyId];
            require(policy.claimsPaid + claim.amount <= policy.coverageLimit, "AgentInsurancePool: exceeds coverage");

            claim.status = ClaimStatus.Paid;
            policy.claimsPaid += claim.amount;
            totalPoolBalance -= claim.amount;
            totalClaimsPaid += claim.amount;

            (bool sent, ) = claim.claimant.call{value: claim.amount}("");
            require(sent, "AgentInsurancePool: payout failed");

            emit ClaimResolved(claimId, ClaimStatus.Paid, claim.amount);
        } else {
            claim.status = ClaimStatus.Denied;
            emit ClaimResolved(claimId, ClaimStatus.Denied, 0);
        }
    }

    // ═══════════════════════════════════════════════════
    // VIEW FUNCTIONS
    // ═══════════════════════════════════════════════════

    /// @notice Calculate the risk tier for an agent based on reputation
    function _calculateRiskTier(address agent) internal view returns (uint256) {
        (uint256 score, , ) = reputationRegistry.getReputation(agent);
        if (score >= LOW_RISK_MIN_REP) return 1;       // Low risk
        if (score >= MEDIUM_RISK_MIN_REP) return 2;     // Medium risk
        return 3;                                        // High risk
    }

    /// @notice Calculate required premium for a policy
    function _calculatePremium(
        uint256 coverageLimit,
        uint256 riskTier,
        uint256 duration
    ) internal pure returns (uint256) {
        uint256 rate;
        if (riskTier == 1) rate = LOW_RISK_RATE;
        else if (riskTier == 2) rate = MEDIUM_RISK_RATE;
        else rate = HIGH_RISK_RATE;

        // Premium = coverage * rate * (duration / 365 days) / 10000
        return (coverageLimit * rate * duration) / (365 days * 10000);
    }

    /// @notice Get premium quote for an agent
    function getPremiumQuote(
        address agent,
        uint256 coverageLimit,
        uint256 duration
    ) external view returns (uint256 premium, uint256 riskTier) {
        riskTier = _calculateRiskTier(agent);
        premium = _calculatePremium(coverageLimit, riskTier, duration);
    }

    /// @notice Get all policy IDs for an agent
    function getAgentPolicies(address agent) external view returns (uint256[] memory) {
        return agentPolicies[agent];
    }

    /// @notice Get pool stats
    function getPoolStats() external view returns (
        uint256 balance,
        uint256 policyCount,
        uint256 claimCount,
        uint256 paidOut
    ) {
        return (totalPoolBalance, totalPolicies, totalClaims, totalClaimsPaid);
    }

    // ═══════════════════════════════════════════════════
    // ADMIN
    // ═══════════════════════════════════════════════════

    /// @notice Update the arbiter address
    function setArbiter(address newArbiter) external onlyOwner {
        require(newArbiter != address(0), "AgentInsurancePool: zero arbiter");
        emit ArbiterUpdated(arbiter, newArbiter);
        arbiter = newArbiter;
    }

    /// @notice Transfer ownership
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "AgentInsurancePool: zero owner");
        owner = newOwner;
    }
}
