import {
  PlatformAuthContract,
  PlatformCapabilitiesContract,
  UserAgentsContract,
  UserDevicesContract,
  UserRegistryContract,
  UserRuntimesContract,
} from '../../contracts/WellKnownServiceContracts';
import type { RuntimeAddressContext } from '../remoting/RuntimeAddressContext';
import type { ScopedIntentResolver } from './ScopedIntentResolver';

export const ACTOR_SERVICES = Symbol.for('@open-matrix/core/ActorServices');

export class ActorServices {
  constructor(
    private readonly resolver: ScopedIntentResolver,
    private readonly address: RuntimeAddressContext,
  ) {}

  readonly user = {
    registry: {
      list: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRegistryContract, 'user');
        return service.invoke('registry.list', {
          ...this.userAuthorityPayload(),
          ...payload,
        });
      },

      query: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRegistryContract, 'user');
        return service.invoke('registry.query', {
          ...this.userAuthorityPayload(),
          includeStale: true,
          ...payload,
        });
      },
    },

    devices: {
      list: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserDevicesContract, 'account');
        return service.invoke('devices.list', {
          ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
          ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
          includeOffline: true,
          ...payload,
        });
      },

      get: async (deviceId: string) => {
        const service = await this.resolver.resolve(UserDevicesContract, 'account');
        return service.invoke('devices.get', {
          deviceId,
          ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
          ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
        });
      },

      revoke: async (deviceId: string) => {
        const service = await this.resolver.resolve(UserDevicesContract, 'account');
        return service.invoke('devices.revoke', {
          deviceId,
          ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
          ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
        });
      },
    },

    runtimes: {
      list: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRuntimesContract, 'user');
        return service.invoke('runtimes.list', {
          ...this.userAuthorityPayload(),
          ...this.signedBrowserSessionPayload(),
          includeStale: true,
          includeOffline: true,
          ...payload,
        });
      },

      registered: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRuntimesContract, 'user');
        return service.invoke('runtimes.registered', {
          ...this.userAuthorityPayload(),
          ...this.signedBrowserSessionPayload(),
          includeStale: true,
          includeOffline: true,
          ...payload,
        });
      },

      introspectMount: async (mount: string, payload: Record<string, unknown> = {}) =>
        this.resolver.invokeUserMount(mount, '$introspect', {
          depth: 'full',
          ...payload,
        }),
    },

    agents: {
      sessions: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserAgentsContract, 'user');
        return service.invoke('$sessionList', payload);
      },

      readSession: async (sessionId: string) => {
        const service = await this.resolver.resolve(UserAgentsContract, 'user');
        return service.invoke('session_state.read', { sessionId });
      },
    },
  };

  readonly platform = {
    auth: {
      currentSession: async () => {
        const service = await this.resolver.resolve(PlatformAuthContract, 'platform');
        return service.invoke('session.current', {});
      },

      accountDevices: {
        list: async (payload: Record<string, unknown> = {}) => {
          const service = await this.resolver.resolve(PlatformAuthContract, 'platform');
          return service.invoke('auth.accountDevices.list', {
            ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
            ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
            includeGrants: true,
            ...payload,
          });
        },
      },
    },

    capabilities: {
      check: async (capability: string) => {
        const service = await this.resolver.resolve(PlatformCapabilitiesContract, 'platform');
        return service.invoke('capability.check', { capability });
      },
    },
  };

  space(spaceRoot: string): ActorSpaceServices {
    const root = spaceRoot.trim();
    if (!root) {
      throw new Error('MISSING_SPACE_ROOT');
    }

    return new ActorSpaceServices(this.resolver, this.address, root);
  }

  private requireUserSpaceRoot(): string {
    if (!this.address.userSpaceRoot) {
      throw new Error('MISSING_USER_SPACE_ROOT');
    }
    return this.address.userSpaceRoot;
  }

  private userAuthorityPayload(): Record<string, unknown> {
    const root = this.requireUserSpaceRoot();
    return {
      namespaceRoot: root,
      authorityRoot: root,
    };
  }

  private signedBrowserSessionPayload(): Record<string, unknown> {
    return {
      ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
      ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
    };
  }
}

class ActorSpaceServices {
  constructor(
    private readonly resolver: ScopedIntentResolver,
    private readonly address: RuntimeAddressContext,
    private readonly spaceRoot: string,
  ) {}

  readonly registry = {
    list: async (payload: Record<string, unknown> = {}) => {
      const service = await this.resolver.resolve(UserRegistryContract, { space: this.spaceRoot });
      return service.invoke('registry.list', {
        ...this.spaceAuthorityPayload(),
        ...payload,
      });
    },

    query: async (payload: Record<string, unknown> = {}) => {
      const service = await this.resolver.resolve(UserRegistryContract, { space: this.spaceRoot });
      return service.invoke('registry.query', {
        ...this.spaceAuthorityPayload(),
        includeStale: true,
        ...payload,
      });
    },
  };

  readonly devices = {
    list: async (payload: Record<string, unknown> = {}) => {
      const service = await this.resolver.resolve(UserDevicesContract, { space: this.spaceRoot });
      return service.invoke('devices.list', {
        authorityRoot: this.spaceRoot,
        ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
        ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
        includeOffline: true,
        ...payload,
      });
    },
  };

  readonly runtimes = {
    list: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRuntimesContract, { space: this.spaceRoot });
        return service.invoke('runtimes.list', {
          ...this.spaceAuthorityPayload(),
          ...this.signedBrowserSessionPayload(),
          includeStale: true,
          includeOffline: true,
          ...payload,
        });
      },

    registered: async (payload: Record<string, unknown> = {}) => {
        const service = await this.resolver.resolve(UserRuntimesContract, { space: this.spaceRoot });
        return service.invoke('runtimes.registered', {
          ...this.spaceAuthorityPayload(),
          ...this.signedBrowserSessionPayload(),
          includeStale: true,
          includeOffline: true,
          ...payload,
        });
    },
  };

  private spaceAuthorityPayload(): Record<string, unknown> {
    return {
      namespaceRoot: this.spaceRoot,
      authorityRoot: this.spaceRoot,
    };
  }

  private signedBrowserSessionPayload(): Record<string, unknown> {
    return {
      ...(this.address.principalId ? { principalId: this.address.principalId } : {}),
      ...(this.address.busToken ? { busToken: this.address.busToken } : {}),
    };
  }
}
