import { describe, test, expect } from "bun:test";
import { CredentialController } from "../../../src/controllers/CredentialController";
import { buildMockCredential } from "../../setup";
import { ValueType } from "../../../src/modules/credentials/credentialTypeDefs";

describe("CredentialController", () => {
  // -------------------------------------------------------------------
  // Constructor & _buildFields
  // -------------------------------------------------------------------
  describe("constructor", () => {
    test("sets public properties from credential data", () => {
      const data = buildMockCredential();
      const ctrl = new CredentialController(data);
      expect(ctrl.id).toBe("cred-1");
      expect(ctrl.name).toBe("Test Credential");
      expect(ctrl.notes).toBeNull();
      expect(ctrl.typeId).toBe("ctype-1");
      expect(ctrl.companyId).toBe("company-1");
      expect(ctrl.subCredentialOfId).toBeNull();
    });

    test("builds fields from type definition", () => {
      const data = buildMockCredential();
      const ctrl = new CredentialController(data);
      expect(Array.isArray(ctrl.fields)).toBe(true);
      expect(ctrl.fields).toHaveLength(2);
    });

    test("plain fields have value from raw data", () => {
      const data = buildMockCredential();
      const ctrl = new CredentialController(data);
      const usernameField = ctrl.fields.find((f: any) => f.id === "username");
      expect(usernameField).toBeDefined();
      expect(usernameField.value).toBe("admin");
      expect(usernameField.secure).toBe(false);
    });

    test("secure fields reference secure value ID", () => {
      const data = buildMockCredential();
      const ctrl = new CredentialController(data);
      const passwordField = ctrl.fields.find((f: any) => f.id === "password");
      expect(passwordField).toBeDefined();
      expect(passwordField.secure).toBe(true);
      expect(passwordField.value).toBe("secure-sv-1");
    });

    test("handles sub-credentials in constructor", () => {
      const subCred = buildMockCredential({
        id: "sub-cred-1",
        name: "Sub Cred",
        subCredentialOfId: "cred-1",
        type: {
          id: "ctype-1",
          name: "Login Credential",
          permissionScope: "credential.login",
          icon: null,
          fields: [
            {
              id: "username",
              name: "Username",
              required: true,
              secure: false,
              valueType: "plain_text",
            },
          ],
        },
        securevalues: [],
        subCredentials: [],
      });
      const parent = buildMockCredential({
        subCredentials: [subCred],
      });
      const ctrl = new CredentialController(parent);
      // The parent should have the sub-credential processed
      expect(ctrl.id).toBe("cred-1");
    });
  });

  // -------------------------------------------------------------------
  // getType / getCompany
  // -------------------------------------------------------------------
  describe("getType() / getCompany()", () => {
    test("getType returns the credential type", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const type = ctrl.getType();
      expect(type.id).toBe("ctype-1");
      expect(type.name).toBe("Login Credential");
    });

    test("getCompany returns the company", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const company = ctrl.getCompany();
      expect(company.id).toBe("company-1");
      expect(company.name).toBe("Test Company");
    });
  });

  // -------------------------------------------------------------------
  // toJson
  // -------------------------------------------------------------------
  describe("toJson()", () => {
    test("returns structured JSON without secure field IDs by default", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const json = ctrl.toJson();
      expect(json.id).toBe("cred-1");
      expect(json.name).toBe("Test Credential");
      expect(json.typeId).toBe("ctype-1");
      expect(json.companyId).toBe("company-1");
      expect(json.type.id).toBe("ctype-1");
      expect(json.company.id).toBe("company-1");
      expect(json.secureFieldIds).toBeUndefined();
    });

    test("includes secure field IDs when includeSecureValues is true", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const json = ctrl.toJson({ includeSecureValues: true });
      expect(json.secureFieldIds).toBeDefined();
      expect(json.secureFieldIds).toContain("password");
    });

    test("includes subCredentialOfId when present", () => {
      const data = buildMockCredential({ subCredentialOfId: "parent-1" });
      const ctrl = new CredentialController(data);
      const json = ctrl.toJson();
      expect(json.subCredentialOfId).toBe("parent-1");
    });

    test("excludes subCredentialOfId when null", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const json = ctrl.toJson();
      expect(json.subCredentialOfId).toBeUndefined();
    });

    test("includes timestamp fields", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const json = ctrl.toJson();
      expect(json.createdAt).toBeDefined();
      expect(json.updatedAt).toBeDefined();
    });

    test("subCredentials is undefined when empty", () => {
      const ctrl = new CredentialController(buildMockCredential());
      const json = ctrl.toJson();
      expect(json.subCredentials).toBeUndefined();
    });
  });

  // -------------------------------------------------------------------
  // Sub-credential field building
  // -------------------------------------------------------------------
  describe("sub-credential field building", () => {
    test("builds fields differently for sub-credentials", () => {
      const subData = buildMockCredential({
        id: "sub-1",
        subCredentialOfId: "parent-1",
        fields: { sub_user: "jdoe" },
        type: {
          id: "ctype-1",
          name: "Login",
          permissionScope: "credential.login",
          icon: null,
          fields: [
            {
              id: "sub_user",
              name: "Sub User",
              required: true,
              secure: false,
              valueType: ValueType.PLAIN_TEXT,
            },
          ],
        },
        securevalues: [
          {
            id: "sv-2",
            name: "sub_pass",
            content: "enc",
            hash: "hash",
            credentialId: "sub-1",
            createdAt: new Date("2025-01-01"),
            updatedAt: new Date("2025-01-01"),
          },
        ],
      });
      const ctrl = new CredentialController(subData);
      // Sub-credential fields are built as array with id/value/secure
      expect(Array.isArray(ctrl.fields)).toBe(true);
      const plainField = ctrl.fields.find((f: any) => f.id === "sub_user");
      expect(plainField).toBeDefined();
      expect(plainField.secure).toBe(false);
      const secureField = ctrl.fields.find((f: any) => f.id === "sub_pass");
      expect(secureField).toBeDefined();
      expect(secureField.secure).toBe(true);
    });
  });
});
