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

// ============================================================================
// Route Management State
// ============================================================================

export interface IRouteManagementState {
  mergedRoutes: interfaces.data.IMergedRoute[];
  warnings: interfaces.data.IRouteWarning[];
  httpRedirects: interfaces.data.IHttpRedirectInfo[];
  apiTokens: interfaces.data.IApiTokenInfo[];
  gatewayClients: interfaces.data.IGatewayClient[];
  isLoading: boolean;
  error: string | null;
  lastUpdated: number;
}

export const routeManagementStatePart = await appState.getStatePart<IRouteManagementState>(
  'routeManagement',
  {
    mergedRoutes: [],
    warnings: [],
    httpRedirects: [],
    apiTokens: [],
    gatewayClients: [],
    isLoading: false,
    error: null,
    lastUpdated: 0,
  },
  'soft'
);

// Route Management Actions
// ============================================================================

export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetMergedRoutes
    >('/typedrequest', 'getMergedRoutes');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      ...currentState,
      mergedRoutes: response.routes,
      warnings: response.warnings,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch routes',
    };
  }
});

export const fetchHttpRedirectsAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetHttpRedirects
    >('/typedrequest', 'getHttpRedirects');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      ...currentState,
      httpRedirects: response.redirects,
      isLoading: false,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      isLoading: false,
      error: error instanceof Error ? error.message : 'Failed to fetch HTTP redirects',
    };
  }
});

export const createRouteAction = routeManagementStatePart.createAction<{
  route: any;
  enabled?: boolean;
  metadata?: any;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateRoute
    >('/typedrequest', 'createRoute');

    await request.fire({
      identity: context.identity!,
      route: dataArg.route,
      enabled: dataArg.enabled,
      metadata: dataArg.metadata,
    });

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to create route',
    };
  }
});

export const updateRouteAction = routeManagementStatePart.createAction<{
  id: string;
  route?: any;
  enabled?: boolean;
  metadata?: any;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateRoute
    >('/typedrequest', 'updateRoute');

    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      route: dataArg.route,
      enabled: dataArg.enabled,
      metadata: dataArg.metadata,
    });

    if (!response.success) {
      throw new Error(response.message || 'Failed to update route');
    }

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to update route',
    };
  }
});

export const deleteRouteAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, routeId, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteRoute
      >('/typedrequest', 'deleteRoute');

      const response = await request.fire({
        identity: context.identity!,
        id: routeId,
      });

      if (!response.success) {
        throw new Error(response.message || 'Failed to delete route');
      }

      return await actionContext!.dispatch(fetchMergedRoutesAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete route',
      };
    }
  }
);

export const toggleRouteAction = routeManagementStatePart.createAction<{
  id: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ToggleRoute
    >('/typedrequest', 'toggleRoute');

    const response = await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      enabled: dataArg.enabled,
    });

    if (!response.success) {
      throw new Error(response.message || 'Failed to toggle route');
    }

    return await actionContext!.dispatch(fetchMergedRoutesAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle route',
    };
  }
});

// ============================================================================
// API Token Actions
// ============================================================================

export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListApiTokens
    >('/typedrequest', 'listApiTokens');

    const response = await request.fire({
      identity: context.identity,
    });

    return {
      ...currentState,
      apiTokens: response.tokens,
    };
  } catch (error) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to fetch tokens',
    };
  }
});

export const fetchGatewayClientsAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  if (!context.identity) return currentState;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListGatewayClients
    >('/typedrequest', 'listGatewayClients');
    const response = await request.fire({ identity: context.identity });
    return {
      ...currentState,
      gatewayClients: response.gatewayClients,
      error: null,
      lastUpdated: Date.now(),
    };
  } catch (error) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to fetch gateway clients',
    };
  }
});

export async function createGatewayClient(data: {
  id?: string;
  type: interfaces.data.IGatewayClient['type'];
  name: string;
  description?: string;
  hostnamePatterns?: string[];
  allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
}) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_CreateGatewayClient
  >('/typedrequest', 'createGatewayClient');
  return request.fire({
    identity: context.identity!,
    capabilities: {
      readDomains: true,
      readDnsRecords: true,
      syncRoutes: true,
      syncDnsRecords: false,
      requestCertificates: false,
    },
    ...data,
  });
}

export const updateGatewayClientAction = routeManagementStatePart.createAction<{
  id: string;
  name?: string;
  description?: string;
  hostnamePatterns?: string[];
  allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
  enabled?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;
  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateGatewayClient
    >('/typedrequest', 'updateGatewayClient');
    await request.fire({ identity: context.identity!, ...dataArg });
    return await actionContext!.dispatch(fetchGatewayClientsAction, null);
  } catch (error) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to update gateway client',
    };
  }
});

export const deleteGatewayClientAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, gatewayClientId, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;
    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteGatewayClient
      >('/typedrequest', 'deleteGatewayClient');
      await request.fire({ identity: context.identity!, id: gatewayClientId });
      return await actionContext!.dispatch(fetchGatewayClientsAction, null);
    } catch (error) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to delete gateway client',
      };
    }
  },
);

export async function createGatewayClientToken(
  gatewayClientId: string,
  name?: string,
  expiresInDays?: number | null,
) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_CreateGatewayClientToken
  >('/typedrequest', 'createGatewayClientToken');
  return request.fire({
    identity: context.identity!,
    gatewayClientId,
    name,
    expiresInDays,
  });
}

// Users

export async function createApiToken(
  name: string,
  scopes: interfaces.data.TApiTokenScope[],
  expiresInDays?: number | null,
  policy?: any,
) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_CreateApiToken
  >('/typedrequest', 'createApiToken');

  return request.fire({
    identity: context.identity!,
    name,
    scopes,
    policy,
    expiresInDays,
  });
}

export async function rollApiToken(id: string) {
  const context = getActionContext();
  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_RollApiToken
  >('/typedrequest', 'rollApiToken');

  return request.fire({
    identity: context.identity!,
    id,
  });
}

export const revokeApiTokenAction = routeManagementStatePart.createAction<string>(
  async (statePartArg, tokenId, actionContext): Promise<IRouteManagementState> => {
    const context = getActionContext();
    const currentState = statePartArg.getState()!;

    try {
      const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_RevokeApiToken
      >('/typedrequest', 'revokeApiToken');

      await request.fire({
        identity: context.identity!,
        id: tokenId,
      });

      return await actionContext!.dispatch(fetchApiTokensAction, null);
    } catch (error: unknown) {
      return {
        ...currentState,
        error: error instanceof Error ? error.message : 'Failed to revoke token',
      };
    }
  }
);

export const toggleApiTokenAction = routeManagementStatePart.createAction<{
  id: string;
  enabled: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
  const context = getActionContext();
  const currentState = statePartArg.getState()!;

  try {
    const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ToggleApiToken
    >('/typedrequest', 'toggleApiToken');

    await request.fire({
      identity: context.identity!,
      id: dataArg.id,
      enabled: dataArg.enabled,
    });

    return await actionContext!.dispatch(fetchApiTokensAction, null);
  } catch (error: unknown) {
    return {
      ...currentState,
      error: error instanceof Error ? error.message : 'Failed to toggle token',
    };
  }
});
