import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock apiClient
vi.mock('../client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    patch: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  searchDomains,
  getDomainPrices,
  registerDomain,
  getRegisteredDomains,
  getDomainRegistration,
  updateNameservers,
  toggleAutoRenew,
  renewDomain,
  syncDomain,
  getSearchHistory,
  DomainAvailability,
  DomainPrice,
  DomainRegisterRequest,
  DomainRegistration,
  DomainSearchHistory,
} from '../domains';
import apiClient from '../client';

describe('domains API', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('searchDomains', () => {
    it('searches for domains with default TLDs', async () => {
      const mockResults: DomainAvailability[] = [
        {
          domain: 'example.com',
          available: true,
          price: 12.99,
          premium: false,
          premium_price: null,
        },
        {
          domain: 'example.net',
          available: false,
          price: null,
          premium: false,
          premium_price: null,
        },
        {
          domain: 'example.org',
          available: true,
          price: 14.99,
          premium: false,
          premium_price: null,
        },
      ];
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResults });

      const result = await searchDomains('example');

      expect(apiClient.post).toHaveBeenCalledWith('/domains/search/search/', {
        query: 'example',
        tlds: ['.com', '.net', '.org'],
      });
      expect(result).toEqual(mockResults);
      expect(result).toHaveLength(3);
    });

    it('searches for domains with custom TLDs', async () => {
      const mockResults: DomainAvailability[] = [
        {
          domain: 'mybusiness.io',
          available: true,
          price: 39.99,
          premium: false,
          premium_price: null,
        },
        {
          domain: 'mybusiness.dev',
          available: true,
          price: 12.99,
          premium: false,
          premium_price: null,
        },
      ];
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResults });

      const result = await searchDomains('mybusiness', ['.io', '.dev']);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/search/search/', {
        query: 'mybusiness',
        tlds: ['.io', '.dev'],
      });
      expect(result).toEqual(mockResults);
    });

    it('handles premium domain results', async () => {
      const mockResults: DomainAvailability[] = [
        {
          domain: 'premium.com',
          available: true,
          price: 12.99,
          premium: true,
          premium_price: 5000.0,
        },
      ];
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResults });

      const result = await searchDomains('premium');

      expect(result[0].premium).toBe(true);
      expect(result[0].premium_price).toBe(5000.0);
    });
  });

  describe('getDomainPrices', () => {
    it('fetches domain prices for all TLDs', async () => {
      const mockPrices: DomainPrice[] = [
        {
          tld: '.com',
          registration: 12.99,
          renewal: 14.99,
          transfer: 12.99,
        },
        {
          tld: '.net',
          registration: 14.99,
          renewal: 16.99,
          transfer: 14.99,
        },
        {
          tld: '.org',
          registration: 14.99,
          renewal: 16.99,
          transfer: 14.99,
        },
        {
          tld: '.io',
          registration: 39.99,
          renewal: 39.99,
          transfer: 39.99,
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockPrices });

      const result = await getDomainPrices();

      expect(apiClient.get).toHaveBeenCalledWith('/domains/search/prices/');
      expect(result).toEqual(mockPrices);
      expect(result).toHaveLength(4);
    });
  });

  describe('registerDomain', () => {
    it('registers a new domain with full contact information', async () => {
      const registerRequest: DomainRegisterRequest = {
        domain: 'newbusiness.com',
        years: 2,
        whois_privacy: true,
        auto_renew: true,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
        contact: {
          first_name: 'John',
          last_name: 'Doe',
          email: 'john@example.com',
          phone: '+1.5551234567',
          address: '123 Main St',
          city: 'New York',
          state: 'NY',
          zip_code: '10001',
          country: 'US',
        },
        auto_configure: true,
      };

      const mockRegistration: DomainRegistration = {
        id: 1,
        domain: 'newbusiness.com',
        status: 'pending',
        registered_at: null,
        expires_at: null,
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 25.98,
        renewal_price: null,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
        days_until_expiry: null,
        is_expiring_soon: false,
        created_at: '2024-01-15T10:00:00Z',
        registrant_first_name: 'John',
        registrant_last_name: 'Doe',
        registrant_email: 'john@example.com',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockRegistration });

      const result = await registerDomain(registerRequest);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/search/register/', registerRequest);
      expect(result).toEqual(mockRegistration);
      expect(result.status).toBe('pending');
    });

    it('registers domain without optional nameservers', async () => {
      const registerRequest: DomainRegisterRequest = {
        domain: 'simple.com',
        years: 1,
        whois_privacy: false,
        auto_renew: false,
        contact: {
          first_name: 'Jane',
          last_name: 'Smith',
          email: 'jane@example.com',
          phone: '+1.5559876543',
          address: '456 Oak Ave',
          city: 'Boston',
          state: 'MA',
          zip_code: '02101',
          country: 'US',
        },
        auto_configure: false,
      };

      const mockRegistration: DomainRegistration = {
        id: 2,
        domain: 'simple.com',
        status: 'pending',
        registered_at: null,
        expires_at: null,
        auto_renew: false,
        whois_privacy: false,
        purchase_price: 12.99,
        renewal_price: null,
        nameservers: [],
        days_until_expiry: null,
        is_expiring_soon: false,
        created_at: '2024-01-15T10:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockRegistration });

      const result = await registerDomain(registerRequest);

      expect(result.whois_privacy).toBe(false);
      expect(result.auto_renew).toBe(false);
      expect(result.nameservers).toEqual([]);
    });
  });

  describe('getRegisteredDomains', () => {
    it('fetches all registered domains for current business', async () => {
      const mockDomains: DomainRegistration[] = [
        {
          id: 1,
          domain: 'business1.com',
          status: 'active',
          registered_at: '2023-01-15T10:00:00Z',
          expires_at: '2025-01-15T10:00:00Z',
          auto_renew: true,
          whois_privacy: true,
          purchase_price: 12.99,
          renewal_price: 14.99,
          nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
          days_until_expiry: 365,
          is_expiring_soon: false,
          created_at: '2023-01-15T09:00:00Z',
        },
        {
          id: 2,
          domain: 'business2.net',
          status: 'active',
          registered_at: '2024-01-01T10:00:00Z',
          expires_at: '2024-03-01T10:00:00Z',
          auto_renew: false,
          whois_privacy: false,
          purchase_price: 14.99,
          renewal_price: 16.99,
          nameservers: ['ns1.example.com', 'ns2.example.com'],
          days_until_expiry: 30,
          is_expiring_soon: true,
          created_at: '2024-01-01T09:00:00Z',
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockDomains });

      const result = await getRegisteredDomains();

      expect(apiClient.get).toHaveBeenCalledWith('/domains/registrations/');
      expect(result).toEqual(mockDomains);
      expect(result).toHaveLength(2);
      expect(result[1].is_expiring_soon).toBe(true);
    });

    it('handles empty domain list', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const result = await getRegisteredDomains();

      expect(result).toEqual([]);
      expect(result).toHaveLength(0);
    });
  });

  describe('getDomainRegistration', () => {
    it('fetches a single domain registration by ID', async () => {
      const mockDomain: DomainRegistration = {
        id: 5,
        domain: 'example.com',
        status: 'active',
        registered_at: '2023-06-01T10:00:00Z',
        expires_at: '2025-06-01T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com', 'ns3.digitalocean.com'],
        days_until_expiry: 500,
        is_expiring_soon: false,
        created_at: '2023-06-01T09:30:00Z',
        registrant_first_name: 'Alice',
        registrant_last_name: 'Johnson',
        registrant_email: 'alice@example.com',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockDomain });

      const result = await getDomainRegistration(5);

      expect(apiClient.get).toHaveBeenCalledWith('/domains/registrations/5/');
      expect(result).toEqual(mockDomain);
      expect(result.registrant_email).toBe('alice@example.com');
    });

    it('fetches domain with failed status', async () => {
      const mockDomain: DomainRegistration = {
        id: 10,
        domain: 'failed.com',
        status: 'failed',
        registered_at: null,
        expires_at: null,
        auto_renew: false,
        whois_privacy: false,
        purchase_price: null,
        renewal_price: null,
        nameservers: [],
        days_until_expiry: null,
        is_expiring_soon: false,
        created_at: '2024-01-10T10:00:00Z',
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockDomain });

      const result = await getDomainRegistration(10);

      expect(result.status).toBe('failed');
      expect(result.registered_at).toBeNull();
    });
  });

  describe('updateNameservers', () => {
    it('updates nameservers for a domain', async () => {
      const nameservers = [
        'ns1.customdns.com',
        'ns2.customdns.com',
        'ns3.customdns.com',
        'ns4.customdns.com',
      ];
      const mockUpdated: DomainRegistration = {
        id: 3,
        domain: 'updated.com',
        status: 'active',
        registered_at: '2023-01-01T10:00:00Z',
        expires_at: '2024-01-01T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: nameservers,
        days_until_expiry: 100,
        is_expiring_soon: false,
        created_at: '2023-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockUpdated });

      const result = await updateNameservers(3, nameservers);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/3/update_nameservers/', {
        nameservers: nameservers,
      });
      expect(result.nameservers).toEqual(nameservers);
      expect(result.nameservers).toHaveLength(4);
    });

    it('updates to default DigitalOcean nameservers', async () => {
      const nameservers = ['ns1.digitalocean.com', 'ns2.digitalocean.com', 'ns3.digitalocean.com'];
      const mockUpdated: DomainRegistration = {
        id: 7,
        domain: 'reset.com',
        status: 'active',
        registered_at: '2023-01-01T10:00:00Z',
        expires_at: '2024-01-01T10:00:00Z',
        auto_renew: false,
        whois_privacy: false,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: nameservers,
        days_until_expiry: 200,
        is_expiring_soon: false,
        created_at: '2023-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockUpdated });

      const result = await updateNameservers(7, nameservers);

      expect(result.nameservers).toEqual(nameservers);
    });
  });

  describe('toggleAutoRenew', () => {
    it('enables auto-renewal for a domain', async () => {
      const mockUpdated: DomainRegistration = {
        id: 4,
        domain: 'autorenew.com',
        status: 'active',
        registered_at: '2023-01-01T10:00:00Z',
        expires_at: '2024-01-01T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
        days_until_expiry: 150,
        is_expiring_soon: false,
        created_at: '2023-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockUpdated });

      const result = await toggleAutoRenew(4, true);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/4/toggle_auto_renew/', {
        auto_renew: true,
      });
      expect(result.auto_renew).toBe(true);
    });

    it('disables auto-renewal for a domain', async () => {
      const mockUpdated: DomainRegistration = {
        id: 6,
        domain: 'noautorenew.com',
        status: 'active',
        registered_at: '2023-01-01T10:00:00Z',
        expires_at: '2024-01-01T10:00:00Z',
        auto_renew: false,
        whois_privacy: false,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.example.com', 'ns2.example.com'],
        days_until_expiry: 60,
        is_expiring_soon: true,
        created_at: '2023-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockUpdated });

      const result = await toggleAutoRenew(6, false);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/6/toggle_auto_renew/', {
        auto_renew: false,
      });
      expect(result.auto_renew).toBe(false);
    });
  });

  describe('renewDomain', () => {
    it('renews domain for 1 year (default)', async () => {
      const mockRenewed: DomainRegistration = {
        id: 8,
        domain: 'renew.com',
        status: 'active',
        registered_at: '2022-01-01T10:00:00Z',
        expires_at: '2025-01-01T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
        days_until_expiry: 365,
        is_expiring_soon: false,
        created_at: '2022-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockRenewed });

      const result = await renewDomain(8);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/8/renew/', {
        years: 1,
      });
      expect(result).toEqual(mockRenewed);
    });

    it('renews domain for multiple years', async () => {
      const mockRenewed: DomainRegistration = {
        id: 9,
        domain: 'longterm.com',
        status: 'active',
        registered_at: '2022-01-01T10:00:00Z',
        expires_at: '2027-01-01T10:00:00Z',
        auto_renew: false,
        whois_privacy: false,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.example.com', 'ns2.example.com'],
        days_until_expiry: 1095,
        is_expiring_soon: false,
        created_at: '2022-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockRenewed });

      const result = await renewDomain(9, 5);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/9/renew/', {
        years: 5,
      });
      expect(result).toEqual(mockRenewed);
    });

    it('renews domain for 2 years', async () => {
      const mockRenewed: DomainRegistration = {
        id: 11,
        domain: 'twoyears.com',
        status: 'active',
        registered_at: '2023-01-01T10:00:00Z',
        expires_at: '2026-01-01T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.digitalocean.com', 'ns2.digitalocean.com'],
        days_until_expiry: 730,
        is_expiring_soon: false,
        created_at: '2023-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockRenewed });

      const result = await renewDomain(11, 2);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/11/renew/', {
        years: 2,
      });
      expect(result.expires_at).toBe('2026-01-01T10:00:00Z');
    });
  });

  describe('syncDomain', () => {
    it('syncs domain information from NameSilo', async () => {
      const mockSynced: DomainRegistration = {
        id: 12,
        domain: 'synced.com',
        status: 'active',
        registered_at: '2023-05-15T10:00:00Z',
        expires_at: '2024-05-15T10:00:00Z',
        auto_renew: true,
        whois_privacy: true,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: ['ns1.namesilo.com', 'ns2.namesilo.com'],
        days_until_expiry: 120,
        is_expiring_soon: false,
        created_at: '2023-05-15T09:30:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockSynced });

      const result = await syncDomain(12);

      expect(apiClient.post).toHaveBeenCalledWith('/domains/registrations/12/sync/');
      expect(result).toEqual(mockSynced);
    });

    it('syncs domain and updates status', async () => {
      const mockSynced: DomainRegistration = {
        id: 13,
        domain: 'expired.com',
        status: 'expired',
        registered_at: '2020-01-01T10:00:00Z',
        expires_at: '2023-01-01T10:00:00Z',
        auto_renew: false,
        whois_privacy: false,
        purchase_price: 12.99,
        renewal_price: 14.99,
        nameservers: [],
        days_until_expiry: -365,
        is_expiring_soon: false,
        created_at: '2020-01-01T09:00:00Z',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockSynced });

      const result = await syncDomain(13);

      expect(result.status).toBe('expired');
      expect(result.days_until_expiry).toBeLessThan(0);
    });
  });

  describe('getSearchHistory', () => {
    it('fetches domain search history', async () => {
      const mockHistory: DomainSearchHistory[] = [
        {
          id: 1,
          searched_domain: 'example.com',
          was_available: true,
          price: 12.99,
          searched_at: '2024-01-15T10:00:00Z',
        },
        {
          id: 2,
          searched_domain: 'taken.com',
          was_available: false,
          price: null,
          searched_at: '2024-01-15T10:05:00Z',
        },
        {
          id: 3,
          searched_domain: 'premium.com',
          was_available: true,
          price: 5000.0,
          searched_at: '2024-01-15T10:10:00Z',
        },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockHistory });

      const result = await getSearchHistory();

      expect(apiClient.get).toHaveBeenCalledWith('/domains/history/');
      expect(result).toEqual(mockHistory);
      expect(result).toHaveLength(3);
      expect(result[1].was_available).toBe(false);
      expect(result[2].price).toBe(5000.0);
    });

    it('handles empty search history', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: [] });

      const result = await getSearchHistory();

      expect(result).toEqual([]);
      expect(result).toHaveLength(0);
    });
  });
});
