import * as plugins from './plugins.js';
import * as interfaces from '../ts_interfaces/index.js';

// ============================================================================
// Smartstate instance
// ============================================================================
export const appState = new plugins.domtools.plugins.smartstate.Smartstate();

// ============================================================================
// State Part Interfaces
// ============================================================================

export interface ILoginState {
  identity: interfaces.data.IIdentity | null;
  isLoggedIn: boolean;
}

export interface IServerState {
  status: interfaces.data.IServerStatus | null;
  connectionInfo: interfaces.data.IConnectionInfo | null;
}

export interface IBucketsState {
  buckets: interfaces.data.IBucketInfo[];
}

export interface IObjectsState {
  result: interfaces.data.IObjectListResult | null;
  currentBucket: string;
  currentPrefix: string;
}

export interface ICredentialsState {
  credentials: interfaces.data.IObjstCredential[];
}

export interface IPoliciesState {
  policies: interfaces.data.INamedPolicy[];
}

export interface IConfigState {
  config: interfaces.data.IServerConfig | null;
  clusterHealth: interfaces.data.IClusterHealth | null;
}

export interface IUiState {
  activeView: string;
  autoRefresh: boolean;
  refreshInterval: number;
}

// ============================================================================
// State Parts
// ============================================================================

export const loginStatePart = await appState.getStatePart<ILoginState>(
  'login',
  {
    identity: null,
    isLoggedIn: false,
  },
  'soft',
);

export const serverStatePart = await appState.getStatePart<IServerState>(
  'server',
  {
    status: null,
    connectionInfo: null,
  },
  'soft',
);

export const bucketsStatePart = await appState.getStatePart<IBucketsState>(
  'buckets',
  {
    buckets: [],
  },
  'soft',
);

export const objectsStatePart = await appState.getStatePart<IObjectsState>(
  'objects',
  {
    result: null,
    currentBucket: '',
    currentPrefix: '',
  },
  'soft',
);

export const credentialsStatePart = await appState.getStatePart<ICredentialsState>(
  'credentials',
  {
    credentials: [],
  },
  'soft',
);

export const policiesStatePart = await appState.getStatePart<IPoliciesState>(
  'policies',
  {
    policies: [],
  },
  'soft',
);

export const configStatePart = await appState.getStatePart<IConfigState>(
  'config',
  {
    config: null,
    clusterHealth: null,
  },
  'soft',
);

export const uiStatePart = await appState.getStatePart<IUiState>(
  'ui',
  {
    activeView: 'overview',
    autoRefresh: true,
    refreshInterval: 30000,
  },
);

// ============================================================================
// Helpers
// ============================================================================

interface IActionContext {
  identity: interfaces.data.IIdentity | null;
}

const getActionContext = (): IActionContext => {
  return { identity: loginStatePart.getState().identity };
};

// ============================================================================
// Login Actions
// ============================================================================

export const loginAction = loginStatePart.createAction<{
  username: string;
  password: string;
}>(async (statePartArg, dataArg) => {
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_AdminLoginWithUsernameAndPassword
    >('/typedrequest', 'adminLoginWithUsernameAndPassword');

    const response = await typedRequest.fire({
      username: dataArg.username,
      password: dataArg.password,
    });

    return {
      identity: response.identity || null,
      isLoggedIn: !!response.identity,
    };
  } catch (err) {
    console.error('Login failed:', err);
    return { identity: null, isLoggedIn: false };
  }
});

export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    if (context.identity) {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_AdminLogout
      >('/typedrequest', 'adminLogout');
      await typedRequest.fire({ identity: context.identity });
    }
  } catch (err) {
    console.error('Logout error:', err);
  }
  return { identity: null, isLoggedIn: false };
});

// ============================================================================
// Server Status Actions
// ============================================================================

export const fetchServerStatusAction = serverStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetServerStatus
    >('/typedrequest', 'getServerStatus');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { status: response.status, connectionInfo: response.connectionInfo };
  } catch (err) {
    console.error('Failed to fetch server status:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// Buckets Actions
// ============================================================================

export const fetchBucketsAction = bucketsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListBuckets
    >('/typedrequest', 'listBuckets');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { buckets: response.buckets };
  } catch (err) {
    console.error('Failed to fetch buckets:', err);
    return statePartArg.getState();
  }
});

export const createBucketAction = bucketsStatePart.createAction<{ bucketName: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_CreateBucket
      >('/typedrequest', 'createBucket');
      await typedRequest.fire({ identity: context.identity!, bucketName: dataArg.bucketName });
      // Re-fetch buckets list
      const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_ListBuckets
      >('/typedrequest', 'listBuckets');
      const listResp = await listReq.fire({ identity: context.identity! });
      return { buckets: listResp.buckets };
    } catch (err) {
      console.error('Failed to create bucket:', err);
      return statePartArg.getState();
    }
  },
);

export const deleteBucketAction = bucketsStatePart.createAction<{ bucketName: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteBucket
      >('/typedrequest', 'deleteBucket');
      await typedRequest.fire({ identity: context.identity!, bucketName: dataArg.bucketName });
      const state = statePartArg.getState();
      return {
        buckets: state.buckets.filter((b) => b.name !== dataArg.bucketName),
      };
    } catch (err) {
      console.error('Failed to delete bucket:', err);
      return statePartArg.getState();
    }
  },
);

// ============================================================================
// Named Policies Actions
// ============================================================================

export const fetchPoliciesAction = policiesStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListNamedPolicies
    >('/typedrequest', 'listNamedPolicies');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { policies: response.policies };
  } catch (err) {
    console.error('Failed to fetch policies:', err);
    return statePartArg.getState();
  }
});

export const createPolicyAction = policiesStatePart.createAction<{
  name: string;
  description: string;
  statements: interfaces.data.IObjstStatement[];
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_CreateNamedPolicy
    >('/typedrequest', 'createNamedPolicy');
    await typedRequest.fire({
      identity: context.identity!,
      name: dataArg.name,
      description: dataArg.description,
      statements: dataArg.statements,
    });
    // Re-fetch
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListNamedPolicies
    >('/typedrequest', 'listNamedPolicies');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { policies: listResp.policies };
  } catch (err) {
    console.error('Failed to create policy:', err);
    return statePartArg.getState();
  }
});

export const updatePolicyAction = policiesStatePart.createAction<{
  policyId: string;
  name: string;
  description: string;
  statements: interfaces.data.IObjstStatement[];
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_UpdateNamedPolicy
    >('/typedrequest', 'updateNamedPolicy');
    await typedRequest.fire({
      identity: context.identity!,
      policyId: dataArg.policyId,
      name: dataArg.name,
      description: dataArg.description,
      statements: dataArg.statements,
    });
    // Re-fetch
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListNamedPolicies
    >('/typedrequest', 'listNamedPolicies');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { policies: listResp.policies };
  } catch (err) {
    console.error('Failed to update policy:', err);
    return statePartArg.getState();
  }
});

export const deletePolicyAction = policiesStatePart.createAction<{ policyId: string }>(
  async (statePartArg, dataArg) => {
    const context = getActionContext();
    try {
      const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
        interfaces.requests.IReq_DeleteNamedPolicy
      >('/typedrequest', 'deleteNamedPolicy');
      await typedRequest.fire({ identity: context.identity!, policyId: dataArg.policyId });
      const state = statePartArg.getState();
      return { policies: state.policies.filter((p) => p.id !== dataArg.policyId) };
    } catch (err) {
      console.error('Failed to delete policy:', err);
      return statePartArg.getState();
    }
  },
);

// Standalone async functions for policy-bucket management

export const getBucketNamedPolicies = async (bucketName: string) => {
  const context = getActionContext();
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_GetBucketNamedPolicies
  >('/typedrequest', 'getBucketNamedPolicies');
  return await typedRequest.fire({ identity: context.identity!, bucketName });
};

export const attachPolicyToBucket = async (policyId: string, bucketName: string) => {
  const context = getActionContext();
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_AttachPolicyToBucket
  >('/typedrequest', 'attachPolicyToBucket');
  return await typedRequest.fire({ identity: context.identity!, policyId, bucketName });
};

export const detachPolicyFromBucket = async (policyId: string, bucketName: string) => {
  const context = getActionContext();
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_DetachPolicyFromBucket
  >('/typedrequest', 'detachPolicyFromBucket');
  return await typedRequest.fire({ identity: context.identity!, policyId, bucketName });
};

export const getPolicyBuckets = async (policyId: string) => {
  const context = getActionContext();
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_GetPolicyBuckets
  >('/typedrequest', 'getPolicyBuckets');
  return await typedRequest.fire({ identity: context.identity!, policyId });
};

export const setPolicyBuckets = async (policyId: string, bucketNames: string[]) => {
  const context = getActionContext();
  const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
    interfaces.requests.IReq_SetPolicyBuckets
  >('/typedrequest', 'setPolicyBuckets');
  return await typedRequest.fire({ identity: context.identity!, policyId, bucketNames });
};

// ============================================================================
// Objects Actions
// ============================================================================

export const fetchObjectsAction = objectsStatePart.createAction<{
  bucketName: string;
  prefix?: string;
  delimiter?: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListObjects
    >('/typedrequest', 'listObjects');
    const response = await typedRequest.fire({
      identity: context.identity!,
      bucketName: dataArg.bucketName,
      prefix: dataArg.prefix || '',
      delimiter: dataArg.delimiter || '/',
    });
    return {
      result: response.result,
      currentBucket: dataArg.bucketName,
      currentPrefix: dataArg.prefix || '',
    };
  } catch (err) {
    console.error('Failed to fetch objects:', err);
    return statePartArg.getState();
  }
});

export const deleteObjectAction = objectsStatePart.createAction<{
  bucketName: string;
  key: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_DeleteObject
    >('/typedrequest', 'deleteObject');
    await typedRequest.fire({
      identity: context.identity!,
      bucketName: dataArg.bucketName,
      key: dataArg.key,
    });
    // Re-fetch objects
    const state = statePartArg.getState();
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_ListObjects
    >('/typedrequest', 'listObjects');
    const listResp = await listReq.fire({
      identity: context.identity!,
      bucketName: state.currentBucket,
      prefix: state.currentPrefix,
      delimiter: '/',
    });
    return {
      result: listResp.result,
      currentBucket: state.currentBucket,
      currentPrefix: state.currentPrefix,
    };
  } catch (err) {
    console.error('Failed to delete object:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// Credentials Actions
// ============================================================================

export const fetchCredentialsAction = credentialsStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetCredentials
    >('/typedrequest', 'getCredentials');
    const response = await typedRequest.fire({ identity: context.identity! });
    return { credentials: response.credentials };
  } catch (err) {
    console.error('Failed to fetch credentials:', err);
    return statePartArg.getState();
  }
});

export const addCredentialAction = credentialsStatePart.createAction<{
  accessKeyId: string;
  secretAccessKey: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_AddCredential
    >('/typedrequest', 'addCredential');
    await typedRequest.fire({
      identity: context.identity!,
      accessKeyId: dataArg.accessKeyId,
      secretAccessKey: dataArg.secretAccessKey,
    });
    // Re-fetch credentials
    const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetCredentials
    >('/typedrequest', 'getCredentials');
    const listResp = await listReq.fire({ identity: context.identity! });
    return { credentials: listResp.credentials };
  } catch (err) {
    console.error('Failed to add credential:', err);
    return statePartArg.getState();
  }
});

export const removeCredentialAction = credentialsStatePart.createAction<{
  accessKeyId: string;
}>(async (statePartArg, dataArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_RemoveCredential
    >('/typedrequest', 'removeCredential');
    await typedRequest.fire({
      identity: context.identity!,
      accessKeyId: dataArg.accessKeyId,
    });
    const state = statePartArg.getState();
    return {
      credentials: state.credentials.filter((c) => c.accessKeyId !== dataArg.accessKeyId),
    };
  } catch (err) {
    console.error('Failed to remove credential:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// Config Actions
// ============================================================================

export const fetchConfigAction = configStatePart.createAction(async (statePartArg) => {
  const context = getActionContext();
  try {
    const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetServerConfig
    >('/typedrequest', 'getServerConfig');
    const response = await typedRequest.fire({ identity: context.identity! });
    const clusterHealthRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
      interfaces.requests.IReq_GetClusterHealth
    >('/typedrequest', 'getClusterHealth');
    const clusterHealthResponse = await clusterHealthRequest.fire({ identity: context.identity! });
    return { config: response.config, clusterHealth: clusterHealthResponse.clusterHealth };
  } catch (err) {
    console.error('Failed to fetch config:', err);
    return statePartArg.getState();
  }
});

// ============================================================================
// UI Actions
// ============================================================================

export const setActiveViewAction = uiStatePart.createAction<{ view: string }>(
  async (statePartArg, dataArg) => {
    return { ...statePartArg.getState(), activeView: dataArg.view };
  },
);

// ============================================================================
// Auto-refresh system
// ============================================================================

let refreshIntervalHandle: ReturnType<typeof setInterval> | null = null;

const dispatchCombinedRefreshAction = async () => {
  const loginState = loginStatePart.getState();
  if (!loginState.isLoggedIn) return;

  try {
    await serverStatePart.dispatchAction(fetchServerStatusAction, null);
  } catch (_err) {
    // Silently fail on auto-refresh
  }
};

const startAutoRefresh = () => {
  const uiState = uiStatePart.getState();
  const loginState = loginStatePart.getState();

  if (uiState.autoRefresh && loginState.isLoggedIn) {
    if (refreshIntervalHandle) {
      clearInterval(refreshIntervalHandle);
    }
    refreshIntervalHandle = setInterval(() => {
      dispatchCombinedRefreshAction();
    }, uiState.refreshInterval);
  } else {
    if (refreshIntervalHandle) {
      clearInterval(refreshIntervalHandle);
      refreshIntervalHandle = null;
    }
  }
};

uiStatePart.select((s) => s).subscribe(() => startAutoRefresh());
loginStatePart.select((s) => s).subscribe(() => startAutoRefresh());
startAutoRefresh();
