/**
 * Vaultfire LangChain Tools
 *
 * Drop-in tools for LangChain/LangGraph agents.
 * Each tool is a standard LangChain DynamicStructuredTool
 * that any agent can use immediately.
 */

import { z } from 'zod';
import { VaultfireClient, VaultfireClientConfig } from './client';
import { VaultfireChain } from './chains';

// ============================================================================
// Tool Definition Type (compatible with LangChain without hard dependency)
// ============================================================================

export interface VaultfireTool {
  name: string;
  description: string;
  schema: z.ZodObject<any>;
  func: (input: any) => Promise<string>;
}

// ============================================================================
// Tool Factory
// ============================================================================

/**
 * Create all Vaultfire tools for a LangChain agent.
 *
 * @example
 * ```typescript
 * import { createVaultfireTools } from '@vaultfire/langchain';
 * import { tool } from 'langchain';
 *
 * const vfTools = createVaultfireTools({ chain: 'base' });
 * // Use with LangChain's tool() wrapper or directly as DynamicStructuredTools
 * ```
 */
export function createVaultfireTools(config: VaultfireClientConfig = {}): VaultfireTool[] {
  const client = new VaultfireClient(config);

  return [
    createVerifyAgentTool(client),
    createGetStreetCredTool(client),
    createGetAgentInfoTool(client),
    createGetBondsTool(client),
    createGetReputationTool(client),
    createDiscoverAgentsTool(client),
    createGetProtocolStatsTool(client),
    ...(config.signer || config.privateKey
      ? [createRegisterAgentTool(client), createCreateBondTool(client)]
      : []),
  ];
}

/**
 * Create read-only Vaultfire tools (no signer required).
 * Safe to give to any agent — cannot modify on-chain state.
 */
export function createVaultfireReadTools(config: Omit<VaultfireClientConfig, 'signer' | 'privateKey'> = {}): VaultfireTool[] {
  const client = new VaultfireClient(config);
  return [
    createVerifyAgentTool(client),
    createGetStreetCredTool(client),
    createGetAgentInfoTool(client),
    createGetBondsTool(client),
    createGetReputationTool(client),
    createDiscoverAgentsTool(client),
    createGetProtocolStatsTool(client),
  ];
}

// ============================================================================
// Individual Tool Creators
// ============================================================================

function createVerifyAgentTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_verify_agent',
    description:
      'Verify if an AI agent is trustworthy by checking its on-chain identity, ' +
      'partnership bonds, Street Cred score, and reputation on the Vaultfire Protocol. ' +
      'Returns a trust verdict with detailed breakdown. Use this before interacting with any unknown agent.',
    schema: z.object({
      address: z.string().describe('Ethereum address of the agent to verify'),
      min_score: z.number().optional().describe('Minimum Street Cred score to be considered trusted (default: 20, max: 95)'),
    }),
    func: async ({ address, min_score }: { address: string; min_score?: number }) => {
      try {
        const result = await client.verifyTrust(address, min_score || 20);
        return JSON.stringify({
          trusted: result.trusted,
          reason: result.reason,
          street_cred: {
            score: result.streetCred.score,
            tier: result.streetCred.tier,
            max_possible: 95,
          },
          identity: {
            registered: result.isRegistered,
            active: result.isActive,
          },
          bonds: {
            has_bonds: result.hasBonds,
            active_count: result.activeBondCount,
          },
          reputation: {
            average_rating: result.reputation.averageRating,
            total_feedbacks: result.reputation.totalFeedbacks,
            verified_feedbacks: result.reputation.verifiedFeedbacks,
          },
          cross_chain_recognized: result.recognizedOnHub,
          chain: result.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createGetStreetCredTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_get_street_cred',
    description:
      'Get the Street Cred score (0-95) and tier for an AI agent on Vaultfire Protocol. ' +
      'Street Cred measures on-chain trust from identity registration, partnership bonds, and stake amounts. ' +
      'Tiers: Unranked (<20), Bronze (20+), Silver (40+), Gold (60+), Platinum (80+).',
    schema: z.object({
      address: z.string().describe('Ethereum address of the agent'),
    }),
    func: async ({ address }: { address: string }) => {
      try {
        const cred = await client.getStreetCred(address);
        return JSON.stringify({
          address: cred.address,
          score: cred.score,
          tier: cred.tier,
          max_possible: 95,
          breakdown: cred.breakdown,
          chain: cred.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createGetAgentInfoTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_get_agent',
    description:
      'Get on-chain identity information for a registered AI agent on Vaultfire Protocol (ERC-8004). ' +
      'Returns the agent URI, type, registration date, and active status.',
    schema: z.object({
      address: z.string().describe('Ethereum address of the agent'),
    }),
    func: async ({ address }: { address: string }) => {
      try {
        const agent = await client.getAgent(address);
        return JSON.stringify({
          address: agent.address,
          agent_uri: agent.agentURI,
          agent_type: agent.agentType,
          active: agent.active,
          registered_at: new Date(agent.registeredAt * 1000).toISOString(),
          chain: agent.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createGetBondsTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_get_bonds',
    description:
      'Get all partnership bonds for an agent or human on Vaultfire Protocol. ' +
      'Partnership bonds are mutual economic stakes between agents and humans. ' +
      'Returns bond details including partner, stake amount, type, and active status.',
    schema: z.object({
      address: z.string().describe('Ethereum address of the participant'),
    }),
    func: async ({ address }: { address: string }) => {
      try {
        const bondIds = await client.getBondsByParticipant(address);
        const bonds = await Promise.all(
          bondIds.map((id) => client.getBond(id))
        );
        return JSON.stringify({
          address,
          total_bonds: bonds.length,
          active_bonds: bonds.filter((b) => b.active).length,
          bonds: bonds.map((b) => ({
            bond_id: b.bondId,
            human: b.human,
            ai_agent: b.aiAgent,
            type: b.partnershipType,
            stake: b.stakeAmount + ' ' + client.chainConfig.nativeCurrency,
            active: b.active,
            created: new Date(b.createdAt * 1000).toISOString(),
          })),
          chain: client.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createGetReputationTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_get_reputation',
    description:
      'Get on-chain reputation data for an AI agent on Vaultfire Protocol. ' +
      'Returns average rating, total feedbacks, and verified feedback count.',
    schema: z.object({
      address: z.string().describe('Ethereum address of the agent'),
    }),
    func: async ({ address }: { address: string }) => {
      try {
        const rep = await client.getReputation(address);
        return JSON.stringify({
          address: rep.address,
          average_rating: rep.averageRating,
          total_feedbacks: rep.totalFeedbacks,
          verified_feedbacks: rep.verifiedFeedbacks,
          last_updated: rep.lastUpdated > 0
            ? new Date(rep.lastUpdated * 1000).toISOString()
            : 'never',
          chain: rep.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createDiscoverAgentsTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_discover_agents',
    description:
      'Discover AI agents by their capabilities on Vaultfire Protocol. ' +
      'Searches the on-chain registry for agents matching the given capability tags.',
    schema: z.object({
      capabilities: z.array(z.string()).describe(
        'List of capability tags to search for (e.g., ["translation", "code-review"])'
      ),
    }),
    func: async ({ capabilities }: { capabilities: string[] }) => {
      try {
        const agents = await client.discoverAgents(capabilities);
        return JSON.stringify({
          capabilities,
          agents_found: agents.length,
          addresses: agents,
          chain: client.chain,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createGetProtocolStatsTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_protocol_stats',
    description:
      'Get current Vaultfire Protocol statistics: total registered agents, total bonds, ' +
      'total value bonded, and bridge status for the current chain.',
    schema: z.object({}),
    func: async () => {
      try {
        const [totalAgents, totalBonds] = await Promise.all([
          client.getTotalAgents(),
          client.getTotalBonds(),
        ]);

        let totalValue = '0';
        try {
          const val = await (client as any).partnership.totalActiveBondValue();
          totalValue = (await import('ethers')).ethers.formatEther(val);
        } catch {
          // May not be available
        }

        let syncedAgents = 0;
        try {
          const count = await (client as any).bridge.getSyncedAgentCount();
          syncedAgents = Number(count);
        } catch {
          // May not be available
        }

        return JSON.stringify({
          chain: client.chain,
          chain_name: client.chainConfig.name,
          total_registered_agents: totalAgents,
          total_bonds: totalBonds,
          total_bonded_value: totalValue + ' ' + client.chainConfig.nativeCurrency,
          bridge_synced_agents: syncedAgents,
          explorer: client.chainConfig.explorer,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createRegisterAgentTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_register_agent',
    description:
      'Register a new AI agent on-chain via the Vaultfire ERC-8004 Identity Registry. ' +
      'This creates a permanent, verifiable on-chain identity for the agent. ' +
      'REQUIRES a connected wallet with gas funds. This is a write transaction.',
    schema: z.object({
      agent_uri: z.string().describe('URI pointing to agent metadata (e.g., GitHub repo URL)'),
      agent_type: z.string().describe('Type of agent (e.g., "autonomous", "infrastructure", "service")'),
      capabilities: z.array(z.string()).describe('List of capability tags (e.g., ["translation", "summarization"])'),
    }),
    func: async ({
      agent_uri,
      agent_type,
      capabilities,
    }: {
      agent_uri: string;
      agent_type: string;
      capabilities: string[];
    }) => {
      try {
        const result = await client.registerAgent(agent_uri, agent_type, capabilities);
        return JSON.stringify({
          success: true,
          tx_hash: result.txHash,
          agent: {
            address: result.agent.address,
            agent_uri: result.agent.agentURI,
            agent_type: result.agent.agentType,
            active: result.agent.active,
          },
          chain: client.chain,
          explorer_link: `${client.chainConfig.explorer}/tx/${result.txHash}`,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}

function createCreateBondTool(client: VaultfireClient): VaultfireTool {
  return {
    name: 'vaultfire_create_bond',
    description:
      'Create a partnership bond between a human and an AI agent on Vaultfire Protocol. ' +
      'Both parties stake value as a commitment to the partnership. ' +
      'REQUIRES a connected wallet with gas + stake funds. This is a write transaction.',
    schema: z.object({
      ai_agent_address: z.string().describe('Ethereum address of the AI agent partner'),
      partnership_type: z.string().describe(
        'Type of partnership: "collaboration", "delegation", "service-provider", "data-sharing", or "oracle-consumer"'
      ),
      stake_amount: z.string().optional().describe(
        'Amount to stake in native currency (e.g., "0.01" for Bronze tier). Default: "0.001"'
      ),
    }),
    func: async ({
      ai_agent_address,
      partnership_type,
      stake_amount,
    }: {
      ai_agent_address: string;
      partnership_type: string;
      stake_amount?: string;
    }) => {
      try {
        const result = await client.createBond(
          ai_agent_address,
          partnership_type,
          stake_amount || '0.001'
        );
        return JSON.stringify({
          success: true,
          bond_id: result.bondId,
          tx_hash: result.txHash,
          bond: {
            human: result.bond.human,
            ai_agent: result.bond.aiAgent,
            type: result.bond.partnershipType,
            stake: result.bond.stakeAmount + ' ' + client.chainConfig.nativeCurrency,
            active: result.bond.active,
          },
          chain: client.chain,
          explorer_link: `${client.chainConfig.explorer}/tx/${result.txHash}`,
        }, null, 2);
      } catch (error: any) {
        return JSON.stringify({ error: error.message });
      }
    },
  };
}
