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

export interface Tenant {
  id: string;
  code: string;
  name: string;
  type: 'SAAS' | 'ENTERPRISE' | 'TRIAL';
  status: 'ACTIVE' | 'SUSPENDED' | 'CANCELED';
  plan: 'BASIC' | 'PRO' | 'ENTERPRISE';
  maxUsers: number;
  maxShops: number;
  storageQuota: number;
  features: string[];
  config: Record<string, any>;
  expiresAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
}

export interface TenantDomain {
  id: string;
  tenantId: string;
  domain: string;
  type: 'SUBDOMAIN' | 'CUSTOM_DOMAIN';
  status: 'ACTIVE' | 'PENDING' | 'VERIFIED' | 'ERROR';
  sslEnabled: boolean;
  sslExpiry: Date | null;
  primary: boolean;
  verifiedAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
}

export interface TenantUser {
  id: string;
  tenantId: string;
  userId: string;
  role: 'ADMIN' | 'MANAGER' | 'OPERATOR' | 'VIEWER';
  permissions: string[];
  status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
  createdAt: Date;
  updatedAt: Date;
}

export interface TenantSettings {
  id: string;
  tenantId: string;
  key: string;
  value: string;
  category: string;
  updatedAt: Date;
}

export class SaasTenantService {
  private static readonly SUBDOMAIN_REGEX = /^[a-z0-9-]+$/;
  private static readonly DOMAIN_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/;

  static async initTables(): Promise<void> {
    await this.initTenantTable();
    await this.initTenantDomainTable();
    await this.initTenantUserTable();
    await this.initTenantSettingsTable();
  }

  private static async initTenantTable(): Promise<void> {
    const exists = await db.schema.hasTable('cf_saas_tenant');
    if (!exists) {
      logger.info('[SaasTenant] Creating cf_saas_tenant table...');
      await db.schema.createTable('cf_saas_tenant', (table) => {
        table.string('id', 64).primary();
        table.string('code', 32).unique().notNullable();
        table.string('name', 128).notNullable();
        table.enum('type', ['SAAS', 'ENTERPRISE', 'TRIAL']).defaultTo('SAAS');
        table.enum('status', ['ACTIVE', 'SUSPENDED', 'CANCELED']).defaultTo('ACTIVE');
        table.enum('plan', ['BASIC', 'PRO', 'ENTERPRISE']).defaultTo('BASIC');
        table.integer('max_users').defaultTo(5);
        table.integer('max_shops').defaultTo(3);
        table.decimal('storage_quota', 12, 2).defaultTo(1024);
        table.json('features').defaultTo('[]');
        table.json('config').defaultTo('{}');
        table.timestamp('expires_at');
        table.timestamps(true, true);
        
        table.index('code');
        table.index('status');
        table.index('plan');
      });
      logger.info('[SaasTenant] Table cf_saas_tenant created');
    }
  }

  private static async initTenantDomainTable(): Promise<void> {
    const exists = await db.schema.hasTable('cf_saas_tenant_domain');
    if (!exists) {
      logger.info('[SaasTenant] Creating cf_saas_tenant_domain table...');
      await db.schema.createTable('cf_saas_tenant_domain', (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('domain', 255).notNullable();
        table.enum('type', ['SUBDOMAIN', 'CUSTOM_DOMAIN']).notNullable();
        table.enum('status', ['ACTIVE', 'PENDING', 'VERIFIED', 'ERROR']).defaultTo('PENDING');
        table.boolean('ssl_enabled').defaultTo(false);
        table.timestamp('ssl_expiry');
        table.boolean('primary').defaultTo(false);
        table.timestamp('verified_at');
        table.timestamps(true, true);
        
        table.unique('domain');
        table.index('tenant_id');
        table.index('domain');
        table.index('status');
        
        table.foreign('tenant_id').references('id').inTable('cf_saas_tenant').onDelete('CASCADE');
      });
      logger.info('[SaasTenant] Table cf_saas_tenant_domain created');
    }
  }

  private static async initTenantUserTable(): Promise<void> {
    const exists = await db.schema.hasTable('cf_saas_tenant_user');
    if (!exists) {
      logger.info('[SaasTenant] Creating cf_saas_tenant_user table...');
      await db.schema.createTable('cf_saas_tenant_user', (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('user_id', 64).notNullable();
        table.enum('role', ['ADMIN', 'MANAGER', 'OPERATOR', 'VIEWER']).defaultTo('OPERATOR');
        table.json('permissions').defaultTo('[]');
        table.enum('status', ['ACTIVE', 'INACTIVE', 'SUSPENDED']).defaultTo('ACTIVE');
        table.timestamps(true, true);
        
        table.unique(['tenant_id', 'user_id']);
        table.index('tenant_id');
        table.index('user_id');
        table.index('role');
        
        table.foreign('tenant_id').references('id').inTable('cf_saas_tenant').onDelete('CASCADE');
      });
      logger.info('[SaasTenant] Table cf_saas_tenant_user created');
    }
  }

  private static async initTenantSettingsTable(): Promise<void> {
    const exists = await db.schema.hasTable('cf_saas_tenant_settings');
    if (!exists) {
      logger.info('[SaasTenant] Creating cf_saas_tenant_settings table...');
      await db.schema.createTable('cf_saas_tenant_settings', (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('key', 128).notNullable();
        table.text('value').notNullable();
        table.string('category', 64).notNullable();
        table.timestamp('updated_at').defaultTo(db.fn.now());
        
        table.unique(['tenant_id', 'key']);
        table.index('tenant_id');
        table.index('category');
        
        table.foreign('tenant_id').references('id').inTable('cf_saas_tenant').onDelete('CASCADE');
      });
      logger.info('[SaasTenant] Table cf_saas_tenant_settings created');
    }
  }

  static async createTenant(data: {
    code: string;
    name: string;
    type?: 'SAAS' | 'ENTERPRISE' | 'TRIAL';
    plan?: 'BASIC' | 'PRO' | 'ENTERPRISE';
    maxUsers?: number;
    maxShops?: number;
    storageQuota?: number;
    features?: string[];
    config?: Record<string, any>;
    expiresAt?: Date;
  }): Promise<Tenant> {
    const id = `TENANT_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    const [tenant] = await db('cf_saas_tenant')
      .insert({
        id,
        code: data.code,
        name: data.name,
        type: data.type || 'SAAS',
        status: 'ACTIVE',
        plan: data.plan || 'BASIC',
        max_users: data.maxUsers || 5,
        max_shops: data.maxShops || 3,
        storage_quota: data.storageQuota || 1024,
        features: JSON.stringify(data.features || []),
        config: JSON.stringify(data.config || {}),
        expires_at: data.expiresAt || null,
        created_at: new Date(),
        updated_at: new Date(),
      })
      .returning('*');

    logger.info(`[SaasTenant] Created tenant: ${id} (${data.code})`);

    return this.mapToTenant(tenant);
  }

  static async addDomain(tenantId: string, domain: string, type: 'SUBDOMAIN' | 'CUSTOM_DOMAIN' = 'SUBDOMAIN'): Promise<TenantDomain> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    if (!tenant) {
      throw new Error('Tenant not found');
    }

    if (type === 'SUBDOMAIN') {
      if (!this.SUBDOMAIN_REGEX.test(domain)) {
        throw new Error('Invalid subdomain format');
      }
    } else {
      if (!this.DOMAIN_REGEX.test(domain)) {
        throw new Error('Invalid domain format');
      }
    }

    const existingDomain = await db('cf_saas_tenant_domain').where({ domain }).first();
    if (existingDomain) {
      throw new Error('Domain already exists');
    }

    const id = `DOMAIN_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    const [tenantDomain] = await db('cf_saas_tenant_domain')
      .insert({
        id,
        tenant_id: tenantId,
        domain,
        type,
        status: type === 'SUBDOMAIN' ? 'ACTIVE' : 'PENDING',
        ssl_enabled: false,
        primary: false,
        created_at: new Date(),
        updated_at: new Date(),
      })
      .returning('*');

    logger.info(`[SaasTenant] Added domain: ${domain} for tenant: ${tenantId}`);

    return this.mapToTenantDomain(tenantDomain);
  }

  static async verifyDomain(domainId: string): Promise<TenantDomain> {
    const domain = await db('cf_saas_tenant_domain').where({ id: domainId }).first();
    if (!domain) {
      throw new Error('Domain not found');
    }

    await db('cf_saas_tenant_domain')
      .where({ id: domainId })
      .update({
        status: 'VERIFIED',
        verified_at: new Date(),
        updated_at: new Date(),
      });

    const [updatedDomain] = await db('cf_saas_tenant_domain').where({ id: domainId }).returning('*');
    
    logger.info(`[SaasTenant] Verified domain: ${domain.domain}`);

    return this.mapToTenantDomain(updatedDomain);
  }

  static async setPrimaryDomain(domainId: string): Promise<void> {
    const domain = await db('cf_saas_tenant_domain').where({ id: domainId }).first();
    if (!domain) {
      throw new Error('Domain not found');
    }

    await db('cf_saas_tenant_domain')
      .where({ tenant_id: domain.tenant_id })
      .update({ primary: false });

    await db('cf_saas_tenant_domain')
      .where({ id: domainId })
      .update({ primary: true, updated_at: new Date() });

    logger.info(`[SaasTenant] Set primary domain: ${domain.domain}`);
  }

  static async getTenantByDomain(domain: string): Promise<Tenant | null> {
    const tenantDomain = await db('cf_saas_tenant_domain')
      .where({ domain, status: 'ACTIVE' })
      .first();

    if (!tenantDomain) {
      return null;
    }

    const tenant = await db('cf_saas_tenant').where({ id: tenantDomain.tenant_id }).first();
    if (!tenant) {
      return null;
    }

    return this.mapToTenant(tenant);
  }

  static async addUser(tenantId: string, userId: string, role: 'ADMIN' | 'MANAGER' | 'OPERATOR' | 'VIEWER' = 'OPERATOR', permissions: string[] = []): Promise<TenantUser> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    if (!tenant) {
      throw new Error('Tenant not found');
    }

    const existingUser = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .first();

    if (existingUser) {
      throw new Error('User already exists in tenant');
    }

    const userCount = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, status: 'ACTIVE' })
      .count('id as count')
      .first();

    if (Number(userCount?.count) >= tenant.max_users) {
      throw new Error('Maximum user limit reached');
    }

    const id = `TUSER_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    const [tenantUser] = await db('cf_saas_tenant_user')
      .insert({
        id,
        tenant_id: tenantId,
        user_id: userId,
        role,
        permissions: JSON.stringify(permissions),
        status: 'ACTIVE',
        created_at: new Date(),
        updated_at: new Date(),
      })
      .returning('*');

    logger.info(`[SaasTenant] Added user: ${userId} to tenant: ${tenantId} with role: ${role}`);

    return this.mapToTenantUser(tenantUser);
  }

  static async updateUserRole(tenantId: string, userId: string, role: 'ADMIN' | 'MANAGER' | 'OPERATOR' | 'VIEWER', permissions: string[] = []): Promise<TenantUser> {
    const tenantUser = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .first();

    if (!tenantUser) {
      throw new Error('User not found in tenant');
    }

    await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .update({
        role,
        permissions: JSON.stringify(permissions),
        updated_at: new Date(),
      });

    const [updatedUser] = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .returning('*');

    logger.info(`[SaasTenant] Updated user role: ${userId} in tenant: ${tenantId} to ${role}`);

    return this.mapToTenantUser(updatedUser);
  }

  static async getUserTenants(userId: string): Promise<Array<{ tenant: Tenant; user: TenantUser }>> {
    const tenantUsers = await db('cf_saas_tenant_user as tu')
      .join('cf_saas_tenant as t', 'tu.tenant_id', 't.id')
      .where('tu.user_id', userId)
      .where('tu.status', 'ACTIVE')
      .where('t.status', 'ACTIVE')
      .select('t.*', 'tu.id as user_id', 'tu.role', 'tu.permissions', 'tu.status as user_status');

    return tenantUsers.map(row => ({
      tenant: this.mapToTenant(row),
      user: this.mapToTenantUser({
        id: row.user_id,
        tenant_id: row.id,
        user_id: userId,
        role: row.role,
        permissions: row.permissions,
        status: row.user_status,
        created_at: row.created_at,
        updated_at: row.updated_at,
      })
    }));
  }

  static async setSetting(tenantId: string, key: string, value: string, category: string = 'general'): Promise<void> {
    await db('cf_saas_tenant_settings')
      .insert({
        id: `SETTING_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        tenant_id: tenantId,
        key,
        value,
        category,
        updated_at: new Date(),
      })
      .onConflict(['tenant_id', 'key'])
      .merge({
        value,
        category,
        updated_at: new Date(),
      });

    logger.info(`[SaasTenant] Set setting: ${key} for tenant: ${tenantId}`);
  }

  static async getSetting(tenantId: string, key: string): Promise<string | null> {
    const setting = await db('cf_saas_tenant_settings')
      .where({ tenant_id: tenantId, key })
      .first();

    return setting?.value || null;
  }

  static async getSettings(tenantId: string, category?: string): Promise<Record<string, string>> {
    let query = db('cf_saas_tenant_settings').where({ tenant_id: tenantId });
    
    if (category) {
      query = query.where({ category });
    }

    const settings = await query.select('key', 'value');
    
    return settings.reduce((acc, s) => {
      acc[s.key] = s.value;
      return acc;
    }, {} as Record<string, string>);
  }

  static async getTenant(tenantId: string): Promise<Tenant | null> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    return tenant ? this.mapToTenant(tenant) : null;
  }

  static async getTenantByCode(code: string): Promise<Tenant | null> {
    const tenant = await db('cf_saas_tenant').where({ code }).first();
    return tenant ? this.mapToTenant(tenant) : null;
  }

  static async listTenants(filters?: {
    status?: 'ACTIVE' | 'SUSPENDED' | 'CANCELED';
    plan?: 'BASIC' | 'PRO' | 'ENTERPRISE';
    type?: 'SAAS' | 'ENTERPRISE' | 'TRIAL';
  }): Promise<Tenant[]> {
    let query = db('cf_saas_tenant');

    if (filters?.status) {
      query = query.where('status', filters.status);
    }
    if (filters?.plan) {
      query = query.where('plan', filters.plan);
    }
    if (filters?.type) {
      query = query.where('type', filters.type);
    }

    const tenants = await query.orderBy('created_at', 'desc');
    return tenants.map(t => this.mapToTenant(t));
  }

  static async updateTenant(tenantId: string, data: {
    name?: string;
    plan?: 'BASIC' | 'PRO' | 'ENTERPRISE';
    maxUsers?: number;
    maxShops?: number;
    storageQuota?: number;
    features?: string[];
    config?: Record<string, any>;
    expiresAt?: Date;
  }): Promise<Tenant> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    if (!tenant) {
      throw new Error('Tenant not found');
    }

    const updateData: any = { updated_at: new Date() };
    if (data.name !== undefined) updateData.name = data.name;
    if (data.plan !== undefined) updateData.plan = data.plan;
    if (data.maxUsers !== undefined) updateData.max_users = data.maxUsers;
    if (data.maxShops !== undefined) updateData.max_shops = data.maxShops;
    if (data.storageQuota !== undefined) updateData.storage_quota = data.storageQuota;
    if (data.features !== undefined) updateData.features = JSON.stringify(data.features);
    if (data.config !== undefined) updateData.config = JSON.stringify(data.config);
    if (data.expiresAt !== undefined) updateData.expires_at = data.expiresAt;

    await db('cf_saas_tenant').where({ id: tenantId }).update(updateData);

    const [updatedTenant] = await db('cf_saas_tenant').where({ id: tenantId }).returning('*');
    
    logger.info(`[SaasTenant] Updated tenant: ${tenantId}`);

    return this.mapToTenant(updatedTenant);
  }

  static async deleteTenant(tenantId: string): Promise<void> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    if (!tenant) {
      throw new Error('Tenant not found');
    }

    await db('cf_saas_tenant').where({ id: tenantId }).del();
    
    logger.info(`[SaasTenant] Deleted tenant: ${tenantId}`);
  }

  static async updateTenantStatus(tenantId: string, status: 'ACTIVE' | 'SUSPENDED' | 'CANCELED'): Promise<Tenant> {
    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    if (!tenant) {
      throw new Error('Tenant not found');
    }

    await db('cf_saas_tenant')
      .where({ id: tenantId })
      .update({ status, updated_at: new Date() });

    const [updatedTenant] = await db('cf_saas_tenant').where({ id: tenantId }).returning('*');
    
    logger.info(`[SaasTenant] Updated tenant status: ${tenantId} to ${status}`);

    return this.mapToTenant(updatedTenant);
  }

  static async deleteDomain(domainId: string): Promise<void> {
    const domain = await db('cf_saas_tenant_domain').where({ id: domainId }).first();
    if (!domain) {
      throw new Error('Domain not found');
    }

    if (domain.primary) {
      throw new Error('Cannot delete primary domain');
    }

    await db('cf_saas_tenant_domain').where({ id: domainId }).del();
    
    logger.info(`[SaasTenant] Deleted domain: ${domainId}`);
  }

  static async removeUser(tenantId: string, userId: string): Promise<void> {
    const tenantUser = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .first();

    if (!tenantUser) {
      throw new Error('User not found in tenant');
    }

    await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, user_id: userId })
      .del();
    
    logger.info(`[SaasTenant] Removed user: ${userId} from tenant: ${tenantId}`);
  }

  static async getStats(): Promise<{
    totalTenants: number;
    activeTenants: number;
    suspendedTenants: number;
    trialTenants: number;
    totalUsers: number;
    totalShops: number;
    totalDomains: number;
    storageUsed: number;
    storageTotal: number;
  }> {
    const totalTenantsResult = await db('cf_saas_tenant').count('id as count').first();
    const activeTenantsResult = await db('cf_saas_tenant').where({ status: 'ACTIVE' }).count('id as count').first();
    const suspendedTenantsResult = await db('cf_saas_tenant').where({ status: 'SUSPENDED' }).count('id as count').first();
    const trialTenantsResult = await db('cf_saas_tenant').where({ type: 'TRIAL' }).count('id as count').first();
    const totalUsersResult = await db('cf_saas_tenant_user').count('id as count').first();
    const totalDomainsResult = await db('cf_saas_tenant_domain').count('id as count').first();
    
    const totalTenants = totalTenantsResult ? parseInt(totalTenantsResult.count as string) : 0;
    const activeTenants = activeTenantsResult ? parseInt(activeTenantsResult.count as string) : 0;
    const suspendedTenants = suspendedTenantsResult ? parseInt(suspendedTenantsResult.count as string) : 0;
    const trialTenants = trialTenantsResult ? parseInt(trialTenantsResult.count as string) : 0;
    const totalUsers = totalUsersResult ? parseInt(totalUsersResult.count as string) : 0;
    const totalDomains = totalDomainsResult ? parseInt(totalDomainsResult.count as string) : 0;
    
    const tenants = await db('cf_saas_tenant').select('max_shops', 'storage_quota');
    const totalShops = tenants.reduce((sum, t) => sum + (t.max_shops || 0), 0);
    const storageTotal = tenants.reduce((sum, t) => sum + parseFloat(t.storage_quota || 0), 0);
    
    const storageUsed = 0;

    return {
      totalTenants: totalTenants,
      activeTenants: activeTenants,
      suspendedTenants: suspendedTenants,
      trialTenants: trialTenants,
      totalUsers: totalUsers,
      totalShops,
      totalDomains: totalDomains,
      storageUsed,
      storageTotal,
    };
  }

  static async getUsage(tenantId: string): Promise<{
    userId: number;
    shopId: number;
    storageUsed: number;
    apiCalls: number;
    lastActive: Date;
  }> {
    const userCountResult = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId, status: 'ACTIVE' })
      .count('id as count')
      .first();

    const tenant = await db('cf_saas_tenant').where({ id: tenantId }).first();
    
    return {
      userId: Number(userCountResult?.count || 0),
      shopId: tenant?.max_shops || 0,
      storageUsed: 0,
      apiCalls: 0,
      lastActive: new Date(),
    };
  }

  static async getOperationLogs(tenantId: string): Promise<Array<{
    id: string;
    tenantId: string;
    tenantName: string;
    operation: string;
    operator: string;
    timestamp: Date;
    details: string;
  }>> {
    return [];
  }

  static async getTenantDomains(tenantId: string): Promise<TenantDomain[]> {
    const domains = await db('cf_saas_tenant_domain')
      .where({ tenant_id: tenantId })
      .orderBy('primary', 'desc')
      .orderBy('created_at', 'desc');

    return domains.map(d => this.mapToTenantDomain(d));
  }

  static async getTenantUsers(tenantId: string): Promise<TenantUser[]> {
    const users = await db('cf_saas_tenant_user')
      .where({ tenant_id: tenantId })
      .orderBy('created_at', 'desc');

    return users.map(u => this.mapToTenantUser(u));
  }

  private static mapToTenant(row: any): Tenant {
    return {
      id: row.id,
      code: row.code,
      name: row.name,
      type: row.type,
      status: row.status,
      plan: row.plan,
      maxUsers: row.max_users,
      maxShops: row.max_shops,
      storageQuota: parseFloat(row.storage_quota),
      features: JSON.parse(row.features || '[]'),
      config: JSON.parse(row.config || '{}'),
      expiresAt: row.expires_at,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  private static mapToTenantDomain(row: any): TenantDomain {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      domain: row.domain,
      type: row.type,
      status: row.status,
      sslEnabled: row.ssl_enabled,
      sslExpiry: row.ssl_expiry,
      primary: row.primary,
      verifiedAt: row.verified_at,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  private static mapToTenantUser(row: any): TenantUser {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      userId: row.user_id,
      role: row.role,
      permissions: JSON.parse(row.permissions || '[]'),
      status: row.status,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}