import {
  EmailAddress,
  PhoneNumber,
  PostalAddress,
  TINNumber,
  UniqueID,
} from "@/core/common/domain";
import { Maybe, Result } from "@repo/rdx-utils";
import { Account, IAccountProps } from "../aggregates";
import { IAccountRepository } from "../repositories";
import { AccountStatus } from "../value-objects";
import { AccountService } from "./account.service";

const mockAccountRepository: IAccountRepository = {
  accountExists: jest.fn(),
  findAll: jest.fn(),
  findByEmail: jest.fn(),
  findById: jest.fn(),
  create: jest.fn(),
  update: jest.fn(),
};

const sampleAccountPrimitives = {
  id: "c5743279-e1cf-4dd5-baae-6698c8c6183c",
  is_companyr: false,
  name: "Empresa XYZ",
  trade_name: "XYZ Trading",
  tin: "123456789",
  street: "Calle Principal 123",
  city: "Madrid",
  state: "Madrid",
  postal_code: "28001",
  country: "España",
  email: "contacto@xyz.com",
  phone: "+34 600 123 456",
  fax: "+34 600 654 321",
  website: "https://xyz.com",
  legal_record: "Registro Mercantil XYZ",
  default_tax: 21,
  status: "active",
  language_code: "es",
  currency_code: "EUR",
  logo: "https://xyz.com/logo.png",
};

const accountBuilder = (accountData: any) => {
  const idOrError = UniqueID.create(sampleAccountPrimitives.id);
  const tinOrError = TINNumber.create(sampleAccountPrimitives.tin);
  const emailOrError = EmailAddress.create(sampleAccountPrimitives.email);
  const phoneOrError = PhoneNumber.create(sampleAccountPrimitives.phone);
  const faxOrError = PhoneNumber.createNullable(sampleAccountPrimitives.fax);
  const postalAddressOrError = PostalAddress.create({
    street: sampleAccountPrimitives.street,
    city: sampleAccountPrimitives.city,
    state: sampleAccountPrimitives.state,
    postalCode: sampleAccountPrimitives.postal_code,
    country: sampleAccountPrimitives.country,
  });

  const result = Result.combine([
    idOrError,
    tinOrError,
    emailOrError,
    phoneOrError,
    faxOrError,
    postalAddressOrError,
  ]);

  if (result.isFailure) {
    return Result.fail(result.error);
  }

  const validatedData: IAccountProps = {
    status: AccountStatus.createInactive(),
    isFreelancer: sampleAccountPrimitives.is_companyr,
    name: sampleAccountPrimitives.name,
    tradeName: sampleAccountPrimitives.trade_name
      ? Maybe.some(sampleAccountPrimitives.trade_name)
      : Maybe.none(),
    tin: tinOrError.data,
    address: postalAddressOrError.data,
    email: emailOrError.data,
    phone: phoneOrError.data,
    fax: faxOrError.data,
    website: sampleAccountPrimitives.website
      ? Maybe.some(sampleAccountPrimitives.website)
      : Maybe.none(),
    legalRecord: sampleAccountPrimitives.legal_record,
    defaultTax: sampleAccountPrimitives.default_tax,
    langCode: sampleAccountPrimitives.language_code,
    currencyCode: sampleAccountPrimitives.currency_code,
    logo: sampleAccountPrimitives.logo ? Maybe.some(sampleAccountPrimitives.logo) : Maybe.none(),
  };

  return Account.create(validatedData, idOrError.data);
};

describe("AccountService - Integración", () => {
  let accountService: AccountService;
  let activeSampleAccount: Account;
  let inactiveSampleAccount: Account;
  let accountId: UniqueID;

  beforeEach(() => {
    accountService = new AccountService(mockAccountRepository);
    inactiveSampleAccount = accountBuilder(sampleAccountPrimitives).data;
  });

  it("debería activar una cuenta existente", async () => {
    const existingAccount = inactiveSampleAccount;
    (mockAccountRepository.findById as jest.Mock).mockResolvedValue(Result.ok(existingAccount));
    (mockAccountRepository.update as jest.Mock).mockResolvedValue(undefined);

    const result = await accountService.activateAccount(existingAccount.id);

    console.log(result);

    expect(result.isSuccess).toBe(true);
    expect(result.data.isActive).toBeTruthy();
    expect(mockAccountRepository.update).toHaveBeenCalledWith(result.data);
  });

  it("debería desactivar una cuenta existente", async () => {
    const existingAccount = Account.update(inactiveSampleAccount, {
      status: AccountStatus.createActive(),
    }).data;

    (mockAccountRepository.findById as jest.Mock).mockResolvedValue(Result.ok(existingAccount));
    (mockAccountRepository.update as jest.Mock).mockResolvedValue(undefined);

    const result = await accountService.deactivateAccount(existingAccount.id);
    expect(result.isSuccess).toBe(true);
    expect(result.data.isActive).toBeFalsy();
    expect(mockAccountRepository.update).toHaveBeenCalledWith(result.data);
  });

  it("debería retornar error si la cuenta no existe al activar", async () => {
    (mockAccountRepository.findById as jest.Mock).mockResolvedValue(null);

    const result = await accountService.activateAccount(UniqueID.create("", true).data);
    expect(result.isFailure).toBe(true);
    expect(result.error.message).toBe("Account not found");
  });

  it("debería retornar error si la cuenta no existe al desactivar", async () => {
    (mockAccountRepository.findById as jest.Mock).mockResolvedValue(null);

    const result = await accountService.deactivateAccount(UniqueID.create("", true).data);
    expect(result.isFailure).toBe(true);
    expect(result.error.message).toBe("Account not found");
  });
});
