import * as plugins from '../plugins.js';
import * as interfaces from '../../ts_interfaces/index.js';
import { appState } from './shared.js';
import { getActionContext } from './login.js';

// ============================================================================
// Domains State (DNS providers + domains + records)
// ============================================================================

export interface IDomainsState {
  providers: interfaces.data.IDnsProviderPublic[];
  domains: interfaces.data.IDomain[];
  records: interfaces.data.IDnsRecord[];
  /** id of the currently-selected domain in the DNS records subview. */
  selectedDomainId: string | null;
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const domainsStatePart = await appState.getStatePart<IDomainsState>(
  'domains',
  {
    providers: [],
    domains: [],
    records: [],
    selectedDomainId: null,
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft',
);

export const fetchDomainsAndProvidersAction = domainsStatePart.createAction(
  async (statePartArg): Promise<IDomainsState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;
    if (!context.identity) return currentState;

    try {
      const providersRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetDnsProviders
      >('/typedrequest', 'getDnsProviders');
      const domainsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetDomains
      >('/typedrequest', 'getDomains');

      const [providersResponse, domainsResponse] = await Promise.all([
        providersRequest.fire({ identity: context.identity }),
        domainsRequest.fire({ identity: context.identity }),
      ]);

      return {
        ...currentState,
        providers: providersResponse.providers,
        domains: domainsResponse.domains,
        isLoading: false,
        error: null,
        lastUpdated: Date.now(),
      };
    } catch (error: unknown) {
      return {
        ...currentState,
        isLoading: false,
        error: error instanceof Error ? error.message : 'Failed to fetch domains/providers',
      };
    }
  },
);

export const fetchDnsRecordsForDomainAction = domainsStatePart.createAction<{ domainId: string }>(
  async (statePartArg, dataArg): Promise<IDomainsState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;
    if (!context.identity) return currentState;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_GetDnsRecords
      >('/typedrequest', 'getDnsRecords');
      const response = await request.fire({
        identity: context.identity,
        domainId: dataArg.domainId,
      });
      return {
        ...currentState,
        records: response.records,
        selectedDomainId: dataArg.domainId,
        error: null,
      };
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to fetch DNS records',
      };
    }
  },
);

export const createDnsProviderAction = domainsStatePart.createAction<{
  name: string;
  type: interfaces.data.TDnsProviderType;
  credentials: interfaces.data.TDnsProviderCredentials;
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateDnsProvider
    >('/typedrequest', 'createDnsProvider');
    const response = await request.fire({
      identity: context.identity!,
      name: dataArg.name,
      type: dataArg.type,
      credentials: dataArg.credentials,
    });
    if (!response.success) {
      return {
        ...statePartArg.getState()!,
        error: response.message || 'Failed to create provider',
      };
    }
    return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to create provider',
    };
  }
});

export const updateDnsProviderAction = domainsStatePart.createAction<{
  id: string;
  name?: string;
  credentials?: interfaces.data.TDnsProviderCredentials;
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateDnsProvider
    >('/typedrequest', 'updateDnsProvider');
    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      name: dataArg.name,
      credentials: dataArg.credentials,
    });
    if (!response.success) {
      return {
        ...statePartArg.getState()!,
        error: response.message || 'Failed to update provider',
      };
    }
    return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to update provider',
    };
  }
});

export const deleteDnsProviderAction = domainsStatePart.createAction<{ id: string; force?: boolean }>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteDnsProvider
      >('/typedrequest', 'deleteDnsProvider');
      const response = await request.fire({
        identity: context.identity!,
        id: dataArg.id,
        force: dataArg.force,
      });
      if (!response.success) {
        return {
          ...statePartArg.getState()!,
          error: response.message || 'Failed to delete provider',
        };
      }
      return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Failed to delete provider',
      };
    }
  },
);

export const testDnsProviderAction = domainsStatePart.createAction<{ id: string }>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_TestDnsProvider
      >('/typedrequest', 'testDnsProvider');
      await request.fire({ identity: context.identity!, id: dataArg.id });
      return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Failed to test provider',
      };
    }
  },
);

/** One-shot fetch for the import-domain modal. Does NOT modify state. */
export async function fetchProviderDomains(
  providerId: string,
): Promise<{ success: boolean; domains?: interfaces.data.IProviderDomainListing[]; message?: string }> {
  const context = getActionContext();
  if (!context.identity) return { success: false, message: 'Not authenticated' };
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_ListProviderDomains
  >('/typedrequest', 'listProviderDomains');
  return await request.fire({ identity: context.identity, providerId });
}

export const createDcrouterDomainAction = domainsStatePart.createAction<{
  name: string;
  description?: string;
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateDomain
    >('/typedrequest', 'createDomain');
    const response = await request.fire({
      identity: context.identity!,
      name: dataArg.name,
      description: dataArg.description,
    });
    if (!response.success) {
      return { ...statePartArg.getState()!, error: response.message || 'Failed to create domain' };
    }
    return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to create domain',
    };
  }
});

export const importDomainsFromProviderAction = domainsStatePart.createAction<{
  providerId: string;
  domainNames: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ImportDomain
    >('/typedrequest', 'importDomain');
    const response = await request.fire({
      identity: context.identity!,
      providerId: dataArg.providerId,
      domainNames: dataArg.domainNames,
    });
    if (!response.success) {
      return { ...statePartArg.getState()!, error: response.message || 'Failed to import domains' };
    }
    return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to import domains',
    };
  }
});

export const deleteDomainAction = domainsStatePart.createAction<{ id: string }>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteDomain
      >('/typedrequest', 'deleteDomain');
      const response = await request.fire({ identity: context.identity!, id: dataArg.id });
      if (!response.success) {
        return { ...statePartArg.getState()!, error: response.message || 'Failed to delete domain' };
      }
      return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Failed to delete domain',
      };
    }
  },
);

export const syncDomainAction = domainsStatePart.createAction<{ id: string }>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_SyncDomain
      >('/typedrequest', 'syncDomain');
      const response = await request.fire({ identity: context.identity!, id: dataArg.id });
      if (!response.success) {
        return { ...statePartArg.getState()!, error: response.message || 'Failed to sync domain' };
      }
      return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Failed to sync domain',
      };
    }
  },
);

export const migrateDomainAction = domainsStatePart.createAction<{
  id: string;
  targetSource: interfaces.data.TDomainSource;
  targetProviderId?: string;
  deleteExistingProviderRecords?: boolean;
}>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_MigrateDomain
      >('/typedrequest', 'migrateDomain');
      const response = await request.fire({ identity: context.identity!, ...dataArg });
      if (!response.success) {
        return { ...statePartArg.getState()!, error: response.message || 'Migration failed' };
      }
      return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Migration failed',
      };
    }
  },
);

export const createDnsRecordAction = domainsStatePart.createAction<{
  domainId: string;
  name: string;
  type: interfaces.data.TDnsRecordType;
  value: string;
  ttl?: number;
  proxied?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateDnsRecord
    >('/typedrequest', 'createDnsRecord');
    const response = await request.fire({
      identity: context.identity!,
      domainId: dataArg.domainId,
      name: dataArg.name,
      type: dataArg.type,
      value: dataArg.value,
      ttl: dataArg.ttl,
      proxied: dataArg.proxied,
    });
    if (!response.success) {
      return { ...statePartArg.getState()!, error: response.message || 'Failed to create record' };
    }
    return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to create record',
    };
  }
});

export const updateDnsRecordAction = domainsStatePart.createAction<{
  id: string;
  domainId: string;
  name?: string;
  value?: string;
  ttl?: number;
  proxied?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
  const context = getActionContext();
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateDnsRecord
    >('/typedrequest', 'updateDnsRecord');
    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      name: dataArg.name,
      value: dataArg.value,
      ttl: dataArg.ttl,
      proxied: dataArg.proxied,
    });
    if (!response.success) {
      return { ...statePartArg.getState()!, error: response.message || 'Failed to update record' };
    }
    return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
  } catch (error: unknown) {
    return {
      ...statePartArg.getState()!,
      error: error instanceof Error ? error.message : 'Failed to update record',
    };
  }
});

export const deleteDnsRecordAction = domainsStatePart.createAction<{ id: string; domainId: string }>(
  async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
    const context = getActionContext();
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteDnsRecord
      >('/typedrequest', 'deleteDnsRecord');
      const response = await request.fire({ identity: context.identity!, id: dataArg.id });
      if (!response.success) {
        return { ...statePartArg.getState()!, error: response.message || 'Failed to delete record' };
      }
      return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
    } catch (error: unknown) {
      return {
        ...statePartArg.getState()!,
        error: error instanceof Error ? error.message : 'Failed to delete record',
      };
    }
  },
);

// ============================================================================
