import db from '../../config/database';
import { DomainEventBus } from '../../core/runtime/DomainEventBus';
import { logger } from '../../utils/logger';

export interface PlatformAccount {
  id: string;
  tenantId: string;
  platform: string;
  accountName: string;
  accountId: string;
  status: 'ACTIVE' | 'INACTIVE' | 'EXPIRED' | 'ERROR';
  token?: string;
  refreshToken?: string;
  expiresAt?: string;
  shopId?: string;
  config?: Record<string, any>;
  lastSyncAt?: string;
  createdAt: string;
  updatedAt: string;
}

export interface PlatformAccountStats {
  total: number;
  active: number;
  inactive: number;
  expired: number;
  error: number;
}

export class PlatformAccountService {
  private static TABLE = 'cf_platform_account';

  static async initTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('platform', 64).notNullable();
        table.string('account_name', 128).notNullable();
        table.string('account_id', 128).notNullable();
        table.enum('status', ['ACTIVE', 'INACTIVE', 'EXPIRED', 'ERROR']).defaultTo('ACTIVE');
        table.text('token');
        table.text('refresh_token');
        table.timestamp('expires_at');
        table.string('shop_id', 64);
        table.json('config');
        table.timestamp('last_sync_at');
        table.timestamps(true, true);
        table.index(['tenant_id', 'platform']);
        table.index(['shop_id']);
        table.unique(['tenant_id', 'platform', 'account_id']);
      });
    }
  }

  static async list(tenantId: string, filters?: { platform?: string; status?: string; shopId?: string }): Promise<PlatformAccount[]> {
    let query = db(this.TABLE).where('tenant_id', tenantId);
    if (filters?.platform) query = query.where('platform', filters.platform);
    if (filters?.status) query = query.where('status', filters.status);
    if (filters?.shopId) query = query.where('shop_id', filters.shopId);
    return query.orderBy('created_at', 'desc');
  }

  static async getById(id: string): Promise<PlatformAccount | null> {
    const account = await db(this.TABLE).where({ id }).first();
    return account || null;
  }

  static async create(tenantId: string, data: {
    platform: string;
    accountName: string;
    accountId: string;
    token?: string;
    refreshToken?: string;
    expiresAt?: string;
    shopId?: string;
    config?: Record<string, any>;
  }): Promise<PlatformAccount> {
    const id = this.generateId();
    const now = new Date().toISOString();
    const account: PlatformAccount = {
      id,
      tenantId,
      platform: data.platform,
      accountName: data.accountName,
      accountId: data.accountId,
      status: 'ACTIVE',
      token: data.token,
      refreshToken: data.refreshToken,
      expiresAt: data.expiresAt,
      shopId: data.shopId,
      config: data.config,
      createdAt: now,
      updatedAt: now,
    };
    await db(this.TABLE).insert({
      id: account.id,
      tenant_id: account.tenantId,
      platform: account.platform,
      account_name: account.accountName,
      account_id: account.accountId,
      status: account.status,
      token: account.token,
      refresh_token: account.refreshToken,
      expires_at: account.expiresAt,
      shop_id: account.shopId,
      config: account.config ? JSON.stringify(account.config) : null,
      created_at: account.createdAt,
      updated_at: account.updatedAt,
    });
    await DomainEventBus.publish('platform_account.created', { accountId: id, tenantId, platform: data.platform });
    return account;
  }

  static async update(id: string, data: Partial<PlatformAccount>): Promise<PlatformAccount> {
    const updateData: Record<string, any> = { updated_at: new Date().toISOString() };
    if (data.accountName) updateData.account_name = data.accountName;
    if (data.status) updateData.status = data.status;
    if (data.token !== undefined) updateData.token = data.token;
    if (data.refreshToken !== undefined) updateData.refresh_token = data.refreshToken;
    if (data.expiresAt !== undefined) updateData.expires_at = data.expiresAt;
    if (data.shopId !== undefined) updateData.shop_id = data.shopId;
    if (data.config) updateData.config = JSON.stringify(data.config);
    await db(this.TABLE).where({ id }).update(updateData);
    const account = await this.getById(id);
    await DomainEventBus.publish('platform_account.updated', { accountId: id });
    return account!;
  }

  static async delete(id: string): Promise<void> {
    await db(this.TABLE).where({ id }).delete();
    await DomainEventBus.publish('platform_account.deleted', { accountId: id });
  }

  static async refreshToken(id: string): Promise<{ success: boolean; message: string }> {
    const account = await this.getById(id);
    if (!account) return { success: false, message: 'Account not found' };
    if (!account.refreshToken) return { success: false, message: 'No refresh token available' };
    try {
      const newToken = await this.callPlatformRefreshApi(account.platform, account.refreshToken);
      await db(this.TABLE).where({ id }).update({
        token: newToken.token,
        expires_at: newToken.expiresAt,
        status: 'ACTIVE',
        updated_at: new Date().toISOString(),
      });
      await DomainEventBus.publish('platform_account.token_refreshed', { accountId: id });
      return { success: true, message: 'Token refreshed successfully' };
    } catch (error: any) {
      await db(this.TABLE).where({ id }).update({
        status: 'ERROR',
        updated_at: new Date().toISOString(),
      });
      return { success: false, message: error.message };
    }
  }

  static async testConnection(id: string): Promise<{ success: boolean; message: string }> {
    const account = await this.getById(id);
    if (!account) return { success: false, message: 'Account not found' };
    try {
      const isValid = await this.validateToken(account.platform, account.token!);
      if (isValid) {
        await db(this.TABLE).where({ id }).update({ status: 'ACTIVE', updated_at: new Date().toISOString() });
        return { success: true, message: 'Connection successful' };
      }
      await db(this.TABLE).where({ id }).update({ status: 'EXPIRED', updated_at: new Date().toISOString() });
      return { success: false, message: 'Token expired' };
    } catch (error: any) {
      await db(this.TABLE).where({ id }).update({ status: 'ERROR', updated_at: new Date().toISOString() });
      return { success: false, message: error.message };
    }
  }

  static async syncAccount(id: string): Promise<{ success: boolean; message: string }> {
    const account = await this.getById(id);
    if (!account) return { success: false, message: 'Account not found' };
    try {
      await db(this.TABLE).where({ id }).update({
        last_sync_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      });
      await DomainEventBus.publish('platform_account.sync_requested', { accountId: id, platform: account.platform });
      return { success: true, message: 'Sync initiated' };
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  static async getStats(tenantId: string): Promise<PlatformAccountStats> {
    const accounts = await this.list(tenantId);
    return {
      total: accounts.length,
      active: accounts.filter((a) => a.status === 'ACTIVE').length,
      inactive: accounts.filter((a) => a.status === 'INACTIVE').length,
      expired: accounts.filter((a) => a.status === 'EXPIRED').length,
      error: accounts.filter((a) => a.status === 'ERROR').length,
    };
  }

  static generateOAuthUrl(accountId: string, platform: string): string {
    const OAUTH_CONFIG: Record<string, { authUrl: string; clientId: string; scope: string }> = {
      AMAZON: {
        authUrl: 'https://sellercentral.amazon.com/apps/authorize/consent',
        clientId: process.env.AMAZON_CLIENT_ID || '',
        scope: 'sellingpartnerapi::migration',
      },
      EBAY: {
        authUrl: 'https://auth.ebay.com/oauth2/authorize',
        clientId: process.env.EBAY_CLIENT_ID || '',
        scope: 'https://api.ebay.com/oauth/api_scope',
      },
      SHOPIFY: {
        authUrl: `https://{shop}.myshopify.com/admin/oauth/authorize`,
        clientId: process.env.SHOPIFY_CLIENT_ID || '',
        scope: 'read_products,write_products,read_orders,write_orders',
      },
      SHOPEE: {
        authUrl: 'https://partner.shopeemobile.com/api/v2/shop/auth_partner',
        clientId: process.env.SHOPEE_PARTNER_ID || '',
        scope: '',
      },
      TIKTOK: {
        authUrl: 'https://services.tiktokshop.com/open/authorize',
        clientId: process.env.TIKTOK_APP_ID || '',
        scope: '',
      },
    };

    const config = OAUTH_CONFIG[platform.toUpperCase()];
    if (!config) {
      throw new Error(`Unsupported platform: ${platform}`);
    }

    const redirectUri = `${process.env.API_BASE_URL}/api/platform-auth/callback/${platform.toLowerCase()}`;
    const state = Buffer.from(JSON.stringify({ accountId, timestamp: Date.now() })).toString('base64');

    const params = new URLSearchParams({
      client_id: config.clientId,
      redirect_uri: redirectUri,
      response_type: 'code',
      scope: config.scope,
      state,
    });

    return `${config.authUrl}?${params.toString()}`;
  }

  static async handleOAuthCallback(platform: string, code: string, state: string): Promise<{ success: boolean; error?: string }> {
    try {
      const stateData = JSON.parse(Buffer.from(state, 'base64').toString());
      const { accountId } = stateData;

      const account = await this.getById(accountId);
      if (!account) {
        return { success: false, error: 'Account not found' };
      }

      const tokens = await this.exchangeCodeForTokens(platform, code);

      await db(this.TABLE).where({ id: accountId }).update({
        token: tokens.accessToken,
        refresh_token: tokens.refreshToken,
        expires_at: tokens.expiresAt,
        status: 'ACTIVE',
        updated_at: new Date().toISOString(),
      });

      await DomainEventBus.publish('platform_account.authorized', { accountId, platform });

      return { success: true };
    } catch (error: any) {
      logger.error(`[PlatformAccountService] OAuth callback error: ${error.message}`);
      return { success: false, error: error.message };
    }
  }

  static async disconnect(id: string): Promise<void> {
    await db(this.TABLE).where({ id }).update({
      status: 'INACTIVE',
      token: null,
      refresh_token: null,
      updated_at: new Date().toISOString(),
    });

    await DomainEventBus.publish('platform_account.disconnected', { accountId: id });
  }

  private static async exchangeCodeForTokens(platform: string, code: string): Promise<{
    accessToken: string;
    refreshToken: string;
    expiresAt: string;
  }> {
    return {
      accessToken: `access_${platform}_${Date.now()}`,
      refreshToken: `refresh_${platform}_${Date.now()}`,
      expiresAt: new Date(Date.now() + 3600000 * 24 * 30).toISOString(),
    };
  }

  private static async callPlatformRefreshApi(platform: string, refreshToken: string): Promise<{ token: string; expiresAt: string }> {
    return {
      token: `new_token_${Date.now()}`,
      expiresAt: new Date(Date.now() + 3600000).toISOString(),
    };
  }

  private static async validateToken(platform: string, token: string): Promise<boolean> {
    return !!token;
  }

  private static generateId(): string {
    return `pa_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}
