/**
 * Solon SDK (TypeScript) v0.1.0 — Solon Chain 官方 TS SDK（测试网）
 * 依赖：ethers v6（npm i ethers）；合约地址簿/ABI 见 assets/solon-defi.js 或 assets/abi/*.json
 * 用法：
 *   import { SolonSDK } from './solon-sdk';
 *   const sdk = new SolonSDK();
 *   await sdk.connect('0x…');            // 或 sdk.connectProvider(window.ethereum)
 *   await sdk.buy('NVDA', '10');
 */
import {
  JsonRpcProvider, BrowserProvider, Wallet, Contract, formatEther, parseEther,
  type Signer, type Provider, type TransactionReceipt, type Eip1193Provider,
} from 'ethers';

export const SOLON_SDK_VERSION = '0.1.0';
export const SOLON_CHAIN_ID = 51051;
export const SOLON_RPC = 'https://solon.digital/solonrpc/';

export interface StockMeta { symbol: string; name: string; addr: string; }

/** 合约地址簿（与 assets/solon-defi.js 同步） */
export const SOLON_ADDRESSES = {
  feed: '0x864e468C8086283Ac43E2830C88cE0d87f29C381',
  kyc: '0x9ce874DB71b89Ce54a54b203ce04931C5AaA93B0',
  amm: '0x21917d97e699a125696e93b036022F70783a0Ce2',
  irStock: '0x7C66D9e02e75f4C1bb99a302E23F81A4F58fDf56',
  dividend: '0x8aC596e41db553BAE8B82d151A80dc10d9C98351',
  orderbook: '0x86a60247Bdad0fE1b4fdFdeA231af996C8d5189c',
  bridge: '0x36507aB96929181867CE406B7DcAE0114467474f',
  offering: '0xf61Ed0E0964Ac3B2f7d872B03315972BfE97e816',
  gld: '0x0Fe35F9621969B4bBEed7C153660B91d00cC4094',
} as const;

export const SOLON_STOCKS: StockMeta[] = [
  { symbol: 'NVDA', name: 'Tokenized NVIDIA', addr: '0x679cFa7329f8924B9455cb0D961E8CDE62f867D3' },
  { symbol: 'AAPL', name: 'Tokenized Apple', addr: '0x7A349aE2D6Ea3C8Cc2c653D76d0AB3a8e1089035' },
  { symbol: 'TSLA', name: 'Tokenized Tesla', addr: '0xE08961ED138e849f5aB830326E8492528b303D55' },
  { symbol: 'MSFT', name: 'Tokenized Microsoft', addr: '0xaE5c16e714901d55a60C29EFc9A4Dcb5949Ed5E1' },
  { symbol: 'GLD', name: 'Solon Gold Share', addr: '0x0Fe35F9621969B4bBEed7C153660B91d00cC4094' },
];

/** 精简 ABI（只含 SDK 用到的方法；完整 ABI 见 assets/abi/*.json） */
const ABI = {
  feed: ['function price(address) view returns (uint256)'],
  amm: [
    'function quoteToken(address token, uint256 slnIn) view returns (uint256)',
    'function quoteSLN(address token, uint256 tokIn) view returns (uint256)',
    'function swapSLNForToken(address token, uint256 minOut) payable returns (uint256)',
    'function swapTokenForSLN(address token, uint256 tokIn, uint256 minOut) returns (uint256)',
  ],
  ct: [
    'function balanceOf(address) view returns (uint256)',
    'function allowance(address, address) view returns (uint256)',
    'function approve(address, uint256) returns (bool)',
  ],
  orderbook: [
    'function placeBuy(address token, uint256 price, uint256 amount) payable returns (uint256)',
    'function placeSell(address token, uint256 price, uint256 amount) returns (uint256)',
    'function cancel(uint256 id)',
  ],
  dividend: [
    'function nextId() view returns (uint256)',
    'function getDiv(uint256 id) view returns (address token, uint256 perShare, uint256 blk, uint256 funded)',
    'function claimable(uint256 id, address user) view returns (uint256)',
    'function claim(uint256 id)',
  ],
  offering: [
    'function nextId() view returns (uint256)',
    'function offers(uint256 id) view returns (address token, uint256 price, uint256 cap, uint256 sold, uint256 raised, uint256 end, bool live)',
    'function quote(uint256 id, uint256 amount) view returns (uint256)',
    'function subscribe(uint256 id, uint256 amount) payable',
  ],
  bridge: [
    'function deposit(uint256 targetChain, address target) payable returns (uint256)',
    'function release(uint256 id)',
  ],
  ir: ['function isVerified(address) view returns (bool)'],
  kyc: ['function registerSelf()'],
} as const;

export class SolonSDK {
  readonly provider: JsonRpcProvider;
  readonly chainId: number;
  private signer?: Signer;

  constructor(rpc: string = SOLON_RPC, chainId: number = SOLON_CHAIN_ID) {
    this.chainId = chainId;
    this.provider = new JsonRpcProvider(rpc, chainId);
  }

  /* ---------- 连接 ---------- */
  connect(privateKey: string): Promise<string> {
    this.signer = new Wallet(privateKey, this.provider);
    return this.address();
  }
  async connectProvider(web3: Eip1193Provider): Promise<string> {
    this.signer = await new BrowserProvider(web3).getSigner();
    return this.address();
  }
  address(): Promise<string | null> {
    return this.signer ? this.signer.getAddress() : Promise.resolve(null);
  }

  private runner(): Provider | Signer {
    if (!this.signer) throw new Error('[SolonSDK] 未连接签名者');
    return this.signer;
  }
  private c(addr: string, abi: readonly string[]): Contract {
    return new Contract(addr, abi as string[], this.signer ?? this.provider);
  }
  private stock(sym: string): StockMeta {
    const s = SOLON_STOCKS.find(x => x.symbol === sym.toUpperCase());
    if (!s) throw new Error('[SolonSDK] 未知通证符号：' + sym);
    return s;
  }

  /* ---------- 行情 ---------- */
  async price(sym: string): Promise<number> {
    const p = await this.c(SOLON_ADDRESSES.feed, ABI.feed).price(this.stock(sym).addr);
    return Number(formatEther(p));
  }
  async prices(): Promise<Record<string, number>> {
    const out: Record<string, number> = {};
    for (const s of SOLON_STOCKS) if (s.symbol !== 'GLD') out[s.symbol] = await this.price(s.symbol);
    return out;
  }
  async quoteBuy(sym: string, slnAmount: string): Promise<number> {
    const q = await this.c(SOLON_ADDRESSES.amm, ABI.amm).quoteToken(this.stock(sym).addr, parseEther(slnAmount));
    return Number(formatEther(q));
  }
  async quoteSell(sym: string, shares: string): Promise<number> {
    const q = await this.c(SOLON_ADDRESSES.amm, ABI.amm).quoteSLN(this.stock(sym).addr, parseEther(shares));
    return Number(formatEther(q));
  }

  /* ---------- 交易 ---------- */
  buy(sym: string, slnAmount: string): Promise<TransactionReceipt> {
    const amm = this.c(SOLON_ADDRESSES.amm, ABI.amm).connect(this.runner()) as Contract;
    return amm.swapSLNForToken(this.stock(sym).addr, 0n, { value: parseEther(slnAmount) }).then((t: any) => t.wait());
  }
  async sell(sym: string, shares: string): Promise<TransactionReceipt> {
    const s = this.stock(sym);
    const amt = parseEther(shares);
    const ct = this.c(s.addr, ABI.ct).connect(this.runner()) as Contract;
    if ((await ct.allowance(await this.address(), SOLON_ADDRESSES.amm)) < amt)
      await (await ct.approve(SOLON_ADDRESSES.amm, amt)).wait();
    const amm = this.c(SOLON_ADDRESSES.amm, ABI.amm).connect(this.runner()) as Contract;
    return amm.swapTokenForSLN(s.addr, amt, 0n).then((t: any) => t.wait());
  }

  /* ---------- 限价单 ---------- */
  placeBuy(sym: string, priceSln: string, shares: string): Promise<TransactionReceipt> {
    const cost = (parseEther(priceSln) * parseEther(shares)) / parseEther('1');
    const ob = this.c(SOLON_ADDRESSES.orderbook, ABI.orderbook).connect(this.runner()) as Contract;
    return ob.placeBuy(this.stock(sym).addr, parseEther(priceSln), parseEther(shares), { value: cost }).then((t: any) => t.wait());
  }
  async placeSell(sym: string, priceSln: string, shares: string): Promise<TransactionReceipt> {
    const s = this.stock(sym);
    const amt = parseEther(shares);
    const ct = this.c(s.addr, ABI.ct).connect(this.runner()) as Contract;
    if ((await ct.allowance(await this.address(), SOLON_ADDRESSES.orderbook)) < amt)
      await (await ct.approve(SOLON_ADDRESSES.orderbook, amt)).wait();
    const ob = this.c(SOLON_ADDRESSES.orderbook, ABI.orderbook).connect(this.runner()) as Contract;
    return ob.placeSell(s.addr, parseEther(priceSln), amt).then((t: any) => t.wait());
  }
  cancelOrder(id: bigint | number): Promise<TransactionReceipt> {
    const ob = this.c(SOLON_ADDRESSES.orderbook, ABI.orderbook).connect(this.runner()) as Contract;
    return ob.cancel(id).then((t: any) => t.wait());
  }

  /* ---------- 股息 ---------- */
  async dividends(): Promise<any[]> {
    const dv = this.c(SOLON_ADDRESSES.dividend, ABI.dividend);
    const n = Number(await dv.nextId());
    const out = [];
    for (let i = 1; i < n; i++) out.push(await dv.getDiv(i));
    return out;
  }
  async claimable(divId: number, addr?: string): Promise<number> {
    const a = addr ?? (await this.address());
    if (!a) throw new Error('[SolonSDK] 未连接签名者且未提供地址');
    const c = await this.c(SOLON_ADDRESSES.dividend, ABI.dividend).claimable(divId, a);
    return Number(formatEther(c));
  }
  claimDividend(divId: number): Promise<TransactionReceipt> {
    const dv = this.c(SOLON_ADDRESSES.dividend, ABI.dividend).connect(this.runner()) as Contract;
    return dv.claim(divId).then((t: any) => t.wait());
  }

  /* ---------- 一级认购 ---------- */
  async offers(): Promise<any[]> {
    const of = this.c(SOLON_ADDRESSES.offering, ABI.offering);
    const n = Number(await of.nextId());
    const out = [];
    for (let i = 1; i < n; i++) out.push(await of.offers(i));
    return out;
  }
  async subscribe(offerId: number, amount: string): Promise<TransactionReceipt> {
    const of = this.c(SOLON_ADDRESSES.offering, ABI.offering).connect(this.runner()) as Contract;
    const cost = await of.quote(offerId, parseEther(amount));
    return of.subscribe(offerId, parseEther(amount), { value: cost }).then((t: any) => t.wait());
  }

  /* ---------- 跨链桥 ---------- */
  bridgeDeposit(slnAmount: string, targetChain: number, target: string): Promise<TransactionReceipt> {
    const br = this.c(SOLON_ADDRESSES.bridge, ABI.bridge).connect(this.runner()) as Contract;
    return br.deposit(targetChain, target, { value: parseEther(slnAmount) }).then((t: any) => t.wait());
  }
  bridgeClaim(id: number): Promise<TransactionReceipt> {
    const br = this.c(SOLON_ADDRESSES.bridge, ABI.bridge).connect(this.runner()) as Contract;
    return br.release(id).then((t: any) => t.wait());
  }

  /* ---------- 账户 ---------- */
  async balanceSLN(addr?: string): Promise<number> {
    const a = addr ?? (await this.address());
    if (!a) throw new Error('[SolonSDK] 无地址');
    return Number(formatEther(await this.provider.getBalance(a)));
  }
  async balanceOf(sym: string, addr?: string): Promise<number> {
    const a = addr ?? (await this.address());
    if (!a) throw new Error('[SolonSDK] 无地址');
    const b = await this.c(this.stock(sym).addr, ABI.ct).balanceOf(a);
    return Number(formatEther(b));
  }
  isVerified(addr?: string): Promise<boolean> {
    return this.address().then(async me => {
      const a = addr ?? me;
      if (!a) throw new Error('[SolonSDK] 无地址');
      return this.c(SOLON_ADDRESSES.irStock, ABI.ir).isVerified(a);
    });
  }
  registerSelf(): Promise<TransactionReceipt> {
    const kyc = this.c(SOLON_ADDRESSES.kyc, ABI.kyc).connect(this.runner()) as Contract;
    return kyc.registerSelf().then((t: any) => t.wait());
  }

  static get version(): string { return SOLON_SDK_VERSION; }
}

export default SolonSDK;
