// services/api-client.ts
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
// All Rights Reserved

// import env, { getCountryName } from "../config/env.js";
import assert from "node:assert";

import { NextFunction, Request, RequestHandler, Response } from "express";

import { filterText } from "dtp-cleantext";
import { v4 as uuidv4 } from "uuid";

import ApiClient, {
  ApiClientStatus,
  IApiClient,
} from "../models/api-client.js";
import ApiClientLog, { IApiClientLog } from "../models/api-client-log.js";

import { DtpService } from "../lib/service.js";
import { GadgetId } from "@gadget/api";
import { PopulateOptions } from "mongoose";

class ApiClientService extends DtpService {
  private populateApiClient: PopulateOptions[] = [
    {
      path: "user",
      select: "-passwordSalt -password",
    },
  ];

  get name(): string {
    return "ApiClientService";
  }
  get slug(): string {
    return "apiClient";
  }

  constructor() {
    super();
  }

  async start(): Promise<void> {}

  async stop(): Promise<void> {}

  middleware(): RequestHandler {
    return async (
      req: Request,
      res: Response,
      next: NextFunction,
    ): Promise<void> => {
      try {
        const apiClientId = req.header("X-Gadget-Key") as string;
        if (!apiClientId) {
          return next();
        }

        const apiClient = await this.getById(apiClientId);
        if (!apiClient) {
          return next();
        }

        if (apiClient.user && !req.user) {
          req.user = apiClient.user;
          res.locals.user = apiClient.user;
        }

        await this.logRequest(apiClient, req);

        return next();
      } catch (error) {
        this.log.error("failed to process ApiClient request", { error });
        return next(error);
      }
    };
  }

  async create(definition: Partial<IApiClient>): Promise<IApiClient> {
    const NOW = new Date();
    const apiClient = new ApiClient();
    apiClient.createdAt = NOW;
    apiClient.updatedAt = NOW;
    apiClient.status = ApiClientStatus.Active;
    apiClient.user = definition.user;

    assert(definition.name, "ApiClient name is required");
    apiClient.name = filterText(definition.name);

    if (definition.description) {
      apiClient.description = filterText(definition.description);
    }

    apiClient.secret = uuidv4();

    await apiClient.save();

    return apiClient.toObject();
  }

  async setStatus(
    client: IApiClient,
    status: ApiClientStatus,
  ): Promise<IApiClient> {
    this.log.info("updating ApiClient status", { _id: client._id, status });

    const newClient = await ApiClient.findOneAndUpdate(
      { _id: client._id },
      { $set: { status } },
      { new: true },
    );
    if (!newClient) {
      const error = new Error("ApiClient not found");
      error.statusCode = 404;
      throw error;
    }

    return newClient;
  }

  async getById(clientId: GadgetId): Promise<IApiClient | null> {
    const client = await ApiClient.findOne({ _id: clientId })
      .populate(this.populateApiClient)
      .lean();
    return client;
  }

  async logRequest(client: IApiClient, req: Request): Promise<IApiClientLog> {
    const NOW = new Date();
    const log = new ApiClientLog();

    log.client = client._id;
    log.createdAt = NOW;
    log.method = req.method;
    log.url = req.url;

    await log.save();

    return log.toObject();
  }

  async remove(client: IApiClient): Promise<void> {
    this.log.info("removing API client", { _id: client._id });
    await ApiClient.deleteOne({ _id: client._id });
  }
}

export default new ApiClientService();
