import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { setupTestDatabase, type TestDatabase } from '../../test-utils/database';
import { withAdminTransaction } from '@alga-psa/db.js';
import { v4 as uuidv4 } from 'uuid';
import type { Knex } from 'knex';

// Simple database operations that mirror the activity logic without Temporal context
async function createTenantInDB(input: { tenantName: string; email: string; companyName?: string; clientName?: string }) {
  return await withAdminTransaction(async (trx: Knex.Transaction) => {
    const tenantId = uuidv4();
    
    // Create tenant record
    const tenantCompanyName = input.companyName ?? input.tenantName;
    await trx('tenants').insert({
      tenant: tenantId,
      client_name: tenantCompanyName,
      email: input.email,
      created_at: new Date(),
      updated_at: new Date(),
    });

    let clientId: string | undefined;

    const clientName = input.clientName ?? input.companyName;

    // Create default client if client/company name is provided
    if (clientName) {
      clientId = uuidv4();
      
      await trx('clients').insert({
        client_id: clientId,
        tenant: tenantId,
        client_name: clientName,
        is_inactive: false,
        created_at: new Date(),
        updated_at: new Date(),
      });

      // Create tenant-client association
      await trx('tenant_companies').insert({
        tenant: tenantId,
        client_id: clientId,
        is_default: true,
        created_at: new Date(),
        updated_at: new Date(),
      });
    }

    return { tenantId, clientId };
  });
}

async function setupTenantDataInDB(input: {
  tenantId: string;
  adminUserId?: string;
  clientId?: string;
  contractLine?: string;
}) {
  return await withAdminTransaction(async (trx: Knex.Transaction) => {
    const setupSteps: string[] = [];

    // Set up default roles
    const defaultRoles = [
      { name: 'Admin', description: 'Administrator with full access' },
      { name: 'User', description: 'Standard user with limited access' },
      { name: 'Client', description: 'Client user with restricted access' }
    ];

    for (const role of defaultRoles) {
      await trx('roles').insert({
        role_id: uuidv4(),
        tenant: input.tenantId,
        role_name: role.name,
        description: role.description,
        created_at: new Date(),
      });
      setupSteps.push(`Created ${role.name} role`);
    }

    // Set up default statuses (for tickets)
    const defaultStatuses = [
      { name: 'Open', is_closed: false },
      { name: 'In Progress', is_closed: false },
      { name: 'Resolved', is_closed: true },
      { name: 'Closed', is_closed: true }
    ];

    // Need a valid user ID for created_by - create a temporary admin user
    const adminUserId = uuidv4();
    await trx('users').insert({
      user_id: adminUserId,
      tenant: input.tenantId,
      email: 'admin@test.com',
      username: 'admin',
      hashed_password: 'temp_password_hash',
      first_name: 'Admin',
      last_name: 'User',
      created_at: new Date(),
    });

    for (const [index, status] of defaultStatuses.entries()) {
      await trx('statuses').insert({
        status_id: uuidv4(),
        tenant: input.tenantId,
        name: status.name,
        status_type: 'ticket',
        order_number: index + 1,
        created_by: adminUserId,
        is_closed: status.is_closed,
        created_at: new Date(),
      });
    }
    setupSteps.push('Created default statuses');

    return { success: true, setupSteps };
  });
}

describe('Tenant Activities Database Logic', () => {
  let testDb: TestDatabase;

  beforeEach(async () => {
    testDb = await setupTestDatabase();
  });

  afterEach(async () => {
    await testDb.cleanup();
  });

  describe('createTenantInDB', () => {
    it('should create a tenant with basic information', async () => {
      const input = {
        tenantName: 'Test Tenant Inc',
        email: 'admin@testtenant.com',
        companyName: 'Test Company',
        clientName: 'Test Client'
      };

      const result = await createTenantInDB(input);

      expect(result.tenantId).toBeDefined();
      expect(result.clientId).toBeDefined();

      // Verify tenant was created in database
      const tenant = await testDb.getTenant(result.tenantId);
      expect(tenant).toBeDefined();
      expect(tenant.client_name).toBe('Test Company');
      expect(tenant.email).toBe('admin@testtenant.com');

      // Verify client was created
      if (result.clientId) {
        const clients = await testDb.getClientsForTenant(result.tenantId);
        expect(clients).toHaveLength(1);
        expect(clients[0].client_name).toBe('Test Client');
      }
    });

    it('should create a tenant without a client', async () => {
      const input = {
        tenantName: 'Solo Tenant',
        email: 'admin@solotenant.com'
      };

      const result = await createTenantInDB(input);

      expect(result.tenantId).toBeDefined();
      expect(result.clientId).toBeUndefined();

      // Verify tenant was created
      const tenant = await testDb.getTenant(result.tenantId);
      expect(tenant).toBeDefined();
      expect(tenant.client_name).toBe('Solo Tenant');

      // Verify no clients were created
      const clients = await testDb.getClientsForTenant(result.tenantId);
      expect(clients).toHaveLength(0);
    });

    it('should fallback to company name when client name is not provided', async () => {
      const input = {
        tenantName: 'Fallback Tenant',
        email: 'admin@fallback.com',
        companyName: 'Fallback Company'
      };

      const result = await createTenantInDB(input);

      expect(result.tenantId).toBeDefined();
      expect(result.clientId).toBeDefined();

      const tenant = await testDb.getTenant(result.tenantId);
      expect(tenant).toBeDefined();
      expect(tenant.client_name).toBe('Fallback Company');

      const clients = await testDb.getClientsForTenant(result.tenantId);
      expect(clients).toHaveLength(1);
      expect(clients[0].client_name).toBe('Fallback Company');
    });

    it('should handle duplicate tenant names gracefully', async () => {
      const input1 = {
        tenantName: 'Duplicate Test',
        email: 'admin1@duplicate.com'
      };
      const input2 = {
        tenantName: 'Duplicate Test',
        email: 'admin2@duplicate.com'
      };

      // Create first tenant
      const result1 = await createTenantInDB(input1);
      expect(result1.tenantId).toBeDefined();

      // Create second tenant with same name (should succeed with different ID)
      const result2 = await createTenantInDB(input2);
      expect(result2.tenantId).toBeDefined();
      expect(result1.tenantId).not.toBe(result2.tenantId);
    });
  });

  describe('setupTenantDataInDB', () => {
    it('should set up complete tenant data', async () => {
      // First create a tenant
      const createResult = await createTenantInDB({
        tenantName: 'Setup Test Client',
        email: 'admin@setup.com',
        clientName: 'Setup Test Co'
      });

      const setupInput = {
        tenantId: createResult.tenantId,
        adminUserId: 'admin-user-123',
        clientId: createResult.clientId,
        contractLine: 'Enterprise'
      };

      const result = await setupTenantDataInDB(setupInput);

      expect(result.success).toBe(true);
      expect(result.setupSteps).toContain('Created Admin role');
      expect(result.setupSteps).toContain('Created User role');
      expect(result.setupSteps).toContain('Created Client role');
      expect(result.setupSteps).toContain('Created default statuses');

      // Verify roles were created
      const roles = await testDb.getRolesForTenant(createResult.tenantId);
      expect(roles).toHaveLength(3);
      const roleNames = roles.map(r => r.role_name);
      expect(roleNames).toContain('Admin');
      expect(roleNames).toContain('User');
      expect(roleNames).toContain('Client');

      // Verify statuses were created
      const statuses = await testDb.getStatusesForTenant(createResult.tenantId);
      expect(statuses.length).toBeGreaterThan(0);
      const statusNames = statuses.map(s => s.name);
      expect(statusNames).toContain('Open');
      expect(statusNames).toContain('Closed');
    });
  });

  describe('error handling', () => {
    it('should validate input parameters', async () => {
      // Test with invalid email format
      const invalidInput = {
        tenantName: 'Test Client',
        email: 'invalid-email' // Invalid email format
      };

      // Since we're not doing email validation in the DB layer, just test that it succeeds
      const result = await createTenantInDB(invalidInput);
      expect(result.tenantId).toBeDefined();
      
      // Clean up
      await testDb.cleanup();
    });
  });
});
