import * as z from 'zod';
import { ProjectStatus } from '@prisma/client';
import { Role } from './roles';
import UserDatabaseService, { IUserListService } from '../services/db/userList';

const userListService: IUserListService = new UserDatabaseService();

export const Id = z.number().int();
export type IdType = z.infer<typeof Id>;

export const Username = z
    .string()
    .transform((value) => value.toLowerCase())
    .refine(
        async (value) =>
            userListService
                .findUserByUsername(value)
                .then((user) => user === null),
        'Invalid input'
    );

export const Password = z
    .string()
    .min(6)
    .refine((value) => !(value in ['1234', '12345678']));

export const RoleEnum = z.nativeEnum(Role);

export const User = z.object({
    username: z.string().nonempty(),
    passwordHash: z.string().nonempty(),
    mail: z.string().email().optional(),
});

export const Customer = z.object({
    firstname: z.string().nonempty(),
    lastname: z.string().nonempty(),
    street: z.string().nonempty(),
    city: z.string().nonempty(),
    zip: z.number().positive().min(3).max(1000000),
    country: z.string().nonempty(),
    company: z.string().optional(),
    phone: z.string().optional(),
    mail: z.string().email().nonempty(),
});
export type CustomerType = z.infer<typeof Customer>;

export const CustomerCreationInput = Customer.extend({
    username: Username,
});

export const CustomerDbInsert = Customer.merge(User);
export type CustomerDbInsertType = z.infer<typeof CustomerDbInsert>;

export const CustomerUpdateInput = Customer.partial().extend({
    id: Id,
});
export type CustomerUpdateInputType = z.infer<typeof CustomerUpdateInput>;

export const UserToken = z.object({
    id: z.number(),
    username: z.string().nonempty(),
    lastLogin: z.string().or(z.date()).nullable(),
    role: RoleEnum,
    profileId: Id,
});
export type UserTokenType = z.infer<typeof UserToken>;

export const UserLoginInput = z.object({
    username: z.string().nonempty(),
    password: z.string().nonempty(),
    role: RoleEnum,
});
export type UserLoginType = z.infer<typeof UserLoginInput>;

export const Employee = z.object({
    firstname: z.string().nonempty(),
    lastname: z.string().nonempty(),
    mail: z.string().email().nonempty(),
});
export type EmployeeType = z.infer<typeof Employee>;

export const EmployeeCreationInput = Employee.extend({
    username: Username,
    password: Password,
});

export const EmployeeDbInsert = Employee.merge(User);
export type EmployeeDbInsertType = z.infer<typeof EmployeeDbInsert>;

export const EmployeeUpdateInput = Employee.partial().extend({
    id: Id,
});
export type EmployeeUpdateInputType = z.infer<typeof EmployeeUpdateInput>;

// # PROJECT

export const Project = z.object({
    id: Id,
    name: z.string().nonempty(),
    description: z.string().optional(),
    price: z.number().optional(),
    advisorId: Id.optional(),
    //currentStatus: ProjectStatus
});

export const ProjectCreationInput = Project.omit({ id: true }).extend({
    customerId: Id,
});

export const ProjectDbInsert = ProjectCreationInput.extend({
    creatorId: Id,
});
export type ProjectDbInsertType = z.infer<typeof ProjectDbInsert>;

export const ProjectUpdateInput = Project.partial().extend({
    id: Id,
});
export type ProjectUpdateInputType = z.infer<typeof ProjectUpdateInput>;

export const ProjectStatusUpdateInput = z.object({
    projectId: Id,
    profileId: Id,
    status: z.nativeEnum(ProjectStatus),
});
export type ProjectStatusUpdateInputType = z.infer<
    typeof ProjectStatusUpdateInput
>;

// # PROFILE
// TODO extract employee, customer from this
export const Profile = z.object({
    id: Id,
    firstname: z.string().nonempty(),
    lastname: z.string().nonempty(),
    street: z.string().nonempty(),
    city: z.string().nonempty(),
    zip: z.number().positive().min(3).max(1000000),
    country: z.string().nonempty(),
    company: z.string().optional(),
    mail: z.string().email().nonempty(),
    phoneNumber: z.string().optional(),
    projects: z.array(Id).optional(),
    advisedProjects: z.array(Id).optional(),
});
