import { ethers, Wallet, Contract, Signer, ContractRunner } from 'ethers';
import { KAMI721CFactory } from '../factories/KAMI721CFactory';
import { KAMI721C, RoyaltyData } from '../contracts/KAMI721C';
import { colorLog, logStyles, formatKeyValue } from '../utils/console-colors';
import dotenv from 'dotenv';

dotenv.config();

// === Configuration ===
const RPC_URL = process.env.RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const USDC_ADDRESS = process.env.USDC_ADDRESS;
const RECEIVER_1 = process.env.ROYALTY_RECEIVER_1;
const RECEIVER_2 = process.env.ROYALTY_RECEIVER_2;
const RECEIVER_3 = process.env.ROYALTY_RECEIVER_3;
const BASE_RENTAL_PRICE = process.env.BASE_RENTAL_PRICE_PER_DAY ? BigInt(process.env.BASE_RENTAL_PRICE_PER_DAY) : 2000000n; // Default 2 USDC

// Minimal ABI for USDC interactions
const USDC_ABI = [
	'function approve(address spender, uint256 amount) external returns (bool)',
	'function allowance(address owner, address spender) external view returns (uint256)',
	'function decimals() external view returns (uint8)',
];

// --- 1. Deploy Contract Function ---
async function deployContract(wallet: Wallet): Promise<KAMI721C> {
	colorLog.section('1. Deploying Contract');
	if (!USDC_ADDRESS) throw new Error('USDC_ADDRESS environment variable is required.');

	const factory = new KAMI721CFactory(wallet);
	const platformAddress = process.env.PLATFORM_ADDRESS || wallet.address;
	const platformCommission = process.env.PLATFORM_COMMISSION_PERCENTAGE ? parseInt(process.env.PLATFORM_COMMISSION_PERCENTAGE) : 500; // 5%
	const initialMintPrice = process.env.MINT_PRICE ? BigInt(process.env.MINT_PRICE) : 1000000n; // 1 USDC
	const name = process.env.CONTRACT_NAME || 'KAMI Setup Example';
	const symbol = process.env.CONTRACT_SYMBOL || 'KSE';
	const baseURI = process.env.BASE_URI || 'https://api.example.com/setup/';

	console.log('Deploying upgradeable contract via proxy with parameters:');
	console.log(formatKeyValue('USDC Address', USDC_ADDRESS));
	console.log(formatKeyValue('Name', name));
	console.log(formatKeyValue('Symbol', symbol));
	console.log(formatKeyValue('Base URI', baseURI));
	console.log(formatKeyValue('Initial Mint Price', `${ethers.formatUnits(initialMintPrice, 6)} USDC`));
	console.log(formatKeyValue('Platform Address', platformAddress));
	console.log(formatKeyValue('Platform Commission', `${platformCommission / 100}%`));
	console.log(formatKeyValue('Initial Owner', wallet.address));

	const nftContract = await factory.deployUpgradeable(
		USDC_ADDRESS,
		name,
		symbol,
		baseURI,
		initialMintPrice,
		platformAddress,
		platformCommission,
		wallet.address
	);

	const proxyAddress = nftContract.getAddress();
	colorLog.success(`Contract deployed successfully at proxy address: ${logStyles.address(proxyAddress)}`);
	return nftContract;
}

// --- 2. Set Royalties Function ---
async function setRoyalties(nftContract: KAMI721C, receivers: string[]): Promise<void> {
	colorLog.section('2. Setting Royalties');
	if (receivers.length !== 3) {
		throw new Error('This function expects exactly 3 receiver addresses.');
	}

	// Assuming equal split (3333 basis points each, summing to 9999)
	const royaltyData: RoyaltyData[] = [
		{ receiver: receivers[0], feeNumerator: 3333n },
		{ receiver: receivers[1], feeNumerator: 3333n },
		{ receiver: receivers[2], feeNumerator: 3333n },
	];

	const transferRoyaltyPercentage = 1000; // 10% for transfers

	console.log(`Setting mint royalties for 3 receivers: ${receivers.join(', ')}`);
	const mintTx = await nftContract.setMintRoyalties(royaltyData);
	console.log('Mint royalties transaction sent:', mintTx.hash);
	await mintTx.wait();
	colorLog.success('Mint royalties set successfully.');

	console.log(`Setting transfer royalties for 3 receivers: ${receivers.join(', ')}`);
	const transferTx = await nftContract.setTransferRoyalties(royaltyData);
	console.log('Transfer royalties transaction sent:', transferTx.hash);
	await transferTx.wait();
	colorLog.success('Transfer royalties set successfully.');

	console.log(`Setting transfer royalty percentage to ${transferRoyaltyPercentage / 100}%...`);
	const percentageTx = await nftContract.setRoyaltyPercentage(transferRoyaltyPercentage);
	console.log('Royalty percentage transaction sent:', percentageTx.hash);
	await percentageTx.wait();
	colorLog.success('Transfer royalty percentage set successfully.');
}

// --- 3. Mint Token Function ---
async function mintToken(nftContract: KAMI721C, wallet: Wallet): Promise<bigint> {
	colorLog.section('3. Minting Token');
	if (!USDC_ADDRESS) throw new Error('USDC_ADDRESS needed for minting.');

	const mintPrice = await nftContract.mintPrice();
	console.log(`Mint price: ${ethers.formatUnits(mintPrice, 6)} USDC`);

	const usdcContract = new Contract(USDC_ADDRESS, USDC_ABI, wallet);
	const decimals = await usdcContract.decimals();

	// Check allowance
	const allowance = await usdcContract.allowance(wallet.address, nftContract.getAddress());
	console.log(`Current USDC allowance: ${ethers.formatUnits(allowance, decimals)} USDC`);

	if (allowance < mintPrice) {
		console.log('Approving USDC spend...');
		const approveTx = await usdcContract.approve(nftContract.getAddress(), mintPrice);
		console.log('Approval transaction sent:', approveTx.hash);
		await approveTx.wait();
		colorLog.success('USDC Approved');
	} else {
		console.log('Sufficient USDC allowance already set.');
	}

	// Mint
	console.log('Sending mint transaction...');
	const mintTx = await nftContract.mint();
	console.log('Mint transaction sent:', mintTx.hash);
	const receipt = await mintTx.wait();
	colorLog.success(`Token minted successfully in block ${receipt?.blockNumber}`);

	// Determine token ID (simplistic approach)
	const totalSupply = await nftContract.totalSupply();
	const tokenId = totalSupply > 0n ? totalSupply - 1n : 0n;
	console.log(`Minted token ID (estimated): ${tokenId}`);
	return tokenId;
}

// --- 4. Prepare for Sale Function ---
async function prepareForSale(nftContract: KAMI721C, tokenId: bigint): Promise<void> {
	colorLog.section('4. Preparing Token for Sale');
	console.log(`Approving contract ${nftContract.getAddress()} to manage token ID ${tokenId}...`);
	// The owner calls approve, allowing the contract to transfer the token during sellToken
	const approveTx = await nftContract.approve(nftContract.getAddress(), tokenId);
	console.log('Approval transaction sent:', approveTx.hash);
	await approveTx.wait();
	colorLog.success(`Token ${tokenId} approved for sale via contract.`);
	console.log('(Note: Actual sale price is set when calling sellToken)');
}

// --- 5. Log Rental Parameters Function ---
async function logRentalParameters(nftContract: KAMI721C, basePricePerDay: bigint): Promise<void> {
	colorLog.section('5. Logging Rental Parameters');
	const platformCommPercentage = await nftContract.getPlatformCommissionPercentage();
	const commissionAmount = (basePricePerDay * platformCommPercentage) / 10000n;
	const totalRentalPricePerDay = basePricePerDay + commissionAmount;

	console.log('Example Rental Parameters (for 1 day):');
	console.log(formatKeyValue('Base Rental Price', `${ethers.formatUnits(basePricePerDay, 6)} USDC`));
	console.log(formatKeyValue('Platform Commission', `${Number(platformCommPercentage) / 100}%`));
	console.log(formatKeyValue('Commission Amount', `${ethers.formatUnits(commissionAmount, 6)} USDC`));
	console.log(formatKeyValue('Total Price (Renter Pays)', `${ethers.formatUnits(totalRentalPricePerDay, 6)} USDC`));
	console.log('(Note: Actual rental is initiated by calling rentToken with duration and total price)');
}

// --- Main Orchestration Function ---
async function main() {
	console.log('Starting KAMI721C Deployment and Setup Script...');

	if (!RPC_URL || !PRIVATE_KEY) {
		throw new Error('RPC_URL and PRIVATE_KEY environment variables are required.');
	}
	if (!RECEIVER_1 || !RECEIVER_2 || !RECEIVER_3) {
		throw new Error('ROYALTY_RECEIVER_1, ROYALTY_RECEIVER_2, and ROYALTY_RECEIVER_3 environment variables are required.');
	}

	const provider = new ethers.JsonRpcProvider(RPC_URL);
	const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
	colorLog.info(`Using wallet: ${logStyles.address(wallet.address)} on network ${(await provider.getNetwork()).name}`);

	try {
		// 1. Deploy
		const nftContract = await deployContract(wallet);

		// Wait a moment for chain/indexer
		await new Promise((resolve) => setTimeout(resolve, 3000));

		// 2. Set Royalties
		const royaltyReceivers = [RECEIVER_1, RECEIVER_2, RECEIVER_3];
		await setRoyalties(nftContract, royaltyReceivers);

		// 3. Mint Token
		const mintedTokenId = await mintToken(nftContract, wallet);

		// 4. Prepare for Sale
		await prepareForSale(nftContract, mintedTokenId);

		// 5. Log Rental Parameters
		await logRentalParameters(nftContract, BASE_RENTAL_PRICE);

		colorLog.section('Setup Complete!');
		console.log(`Contract Address: ${nftContract.getAddress()}`);
		console.log(`Minted Token ID: ${mintedTokenId}`);
	} catch (error: any) {
		colorLog.error('Script failed:');
		console.error(error.message);
		if (error.reason) console.error('Reason:', error.reason);
		if (error.data) console.error('Data:', error.data);
		process.exit(1);
	}
}

main();
