import { Column, Entity, getRepository, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
import { CompanyEntity } from './company.entity';
import { FactionEntity } from './faction.entity';
import { InventoryEntity } from './inventory.entity';
import { CharacterEntity } from './character.entity';
import { ItemTypeEntity } from './item-type.entity';
import { VehicleRegistryEntity } from './vehicle-registry.entity';
import { VehicleStolenReportEntity } from './vehicle-stolen-reports.entity';
import { VehicleKeyEntity } from './vehicle-keys.entity';
import { FineEntity } from './fine.entity';
import { VehicleImpoundEntity } from './vehicle-impound.entity';
import { InvoiceEntity } from './invoice.entity';
import { VehicleUpgradeEntity } from './vehicle-upgrade.entity';
import { companySystem } from '../features/company';
import { ItemEntity } from './item.entity';
import { vehicleSystem } from '../features/vehicle';
import { VehicleInsuranceEntity } from './vehicle-insurance.entity';

export interface VehicleModification {
    index: number;
    type: number;
    description: string;
}

@Entity('vehicles')
export class VehicleEntity {
    @PrimaryGeneratedColumn() id: number;
    @Column({ type: 'varchar', length: 64 }) model: string;
    @Column({ type: 'float' }) x: number;
    @Column({ type: 'float' }) y: number;
    @Column({ type: 'float' }) z: number;
    @Column({ type: 'float' }) heading: number;
    @Column({ type: 'int', default: 0 }) dimension: number;
    @Column() color: number;
    @Column() color2: number;
    @Column({ type: 'int', nullable: true }) pearlescent_color: number | null;
    @Column({ type: 'varchar', length: 8, nullable: true, default: null }) plate: string | null;
    @Column({ type: 'int', nullable: true, default: 0 }) plate_type: number | null;
    @Column({ type: 'int', nullable: true, default: null }) faction_id: number | null;
    @Column({ type: 'int', nullable: true, default: null }) company_id: number | null;
    @Column({ type: 'int', nullable: true, default: null }) character_id: number | null;
    @Column({ nullable: true }) extras: number;
    @Column({ nullable: false }) livery: number;
    @Column({ type: 'json', nullable: true }) wheels: [type: number, index: number, color?: number] | null;
    @Column({ type: 'int', width: 1, default: 4 }) window_tint: number;
    @Column({ type: 'float', nullable: true, default: null }) suspension: number | null;
    @Column({ type: 'float', nullable: false, default: 10 }) fuel: number;
    @Column({ type: 'float', nullable: false, default: 0 }) dirt: number;
    @Column({ type: 'int', nullable: false, default: 0 }) odometer: number;
    @Column({ type: 'int', nullable: false, default: -1 }) engine_level: number;
    @Column({ type: 'int', nullable: false, default: -1 }) brake_level: number;
    @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) created: Date;
    @Column({ type: 'datetime', nullable: true }) seized: Date | null;
    @Column() created_by: number;
    @Column({ type: 'int', nullable: true, unique: true }) vehicle_order_id: number | null;
    @Column({ type: 'int', nullable: true }) loan_id: number | null;
    @Column({ type: 'int', nullable: true }) rent: number | null;
    @Column({ type: 'int', nullable: true }) rented_by_character_id: number | null;
    @Column({ type: 'datetime', nullable: true }) rented_until: Date | null;
    @Column({ type: 'tinyint', default: 0 }) auto_spawn: boolean;
    @Column({ type: 'tinyint', default: 0 }) no_manual_engine: boolean;
    @Column({ type: 'tinyint', default: 0 }) protected_entry: boolean;
    @Column({ type: 'tinyint', default: 0 }) disable_fuel_consumption: boolean;
    @Column({ type: 'datetime', nullable: true }) last_spawned: Date | null;
    @Column({ type: 'datetime', nullable: true }) last_stolen: Date | null;
    @Column({ type: 'tinyint', default: 0 }) insured: boolean;
    @Column({ type: 'json', nullable: true }) mods: VehicleModification[] | null;

    @ManyToOne(() => CharacterEntity, (character) => character.vehicles)
    @JoinColumn({ name: 'character_id' })
    character?: CharacterEntity;

    @ManyToOne(() => CompanyEntity, (company) => company.vehicles)
    @JoinColumn({ name: 'company_id' })
    company?: CompanyEntity;

    @ManyToOne(() => FactionEntity, (faction) => faction.vehicles)
    @JoinColumn({ name: 'faction_id' })
    faction?: FactionEntity;

    @ManyToMany(() => VehicleUpgradeEntity, {
        eager: true,
    })
    @JoinTable({
        name: 'vehicle_vehicle_upgrades',
        joinColumn: { name: 'vehicle_id' },
        inverseJoinColumn: { name: 'vehicle_upgrade_id' },
    })
    upgrades: VehicleUpgradeEntity[];

    @OneToOne(() => VehicleRegistryEntity, { createForeignKeyConstraints: false })
    @JoinColumn({
        name: 'id',
        referencedColumnName: 'vehicle_id',
    })
    registration?: VehicleRegistryEntity;

    @OneToMany(() => VehicleRegistryEntity, (registry) => registry.vehicle)
    registrations?: VehicleRegistryEntity[];

    @OneToMany(() => VehicleStolenReportEntity, (report) => report.vehicle)
    stolenReports?: VehicleStolenReportEntity[];

    @OneToOne(() => VehicleImpoundEntity, { createForeignKeyConstraints: false })
    @JoinColumn({
        name: 'id',
        referencedColumnName: 'vehicle_id',
    })
    impounded?: VehicleImpoundEntity;

    @OneToMany(() => VehicleImpoundEntity, (impound) => impound.vehicle)
    impounds?: VehicleImpoundEntity[];

    @OneToMany(() => VehicleKeyEntity, (key) => key.vehicle)
    keys: VehicleKeyEntity[];

    @OneToMany(() => FineEntity, (fine) => fine.vehicle)
    fines: FineEntity[];

    @OneToMany(() => InvoiceEntity, (invoice) => invoice.lienVehicle)
    liens: InvoiceEntity[];

    @OneToOne(() => VehicleInsuranceEntity, { createForeignKeyConstraints: false })
    @JoinColumn({
        name: 'id',
        referencedColumnName: 'vehicle_id',
    })
    insurance?: VehicleInsuranceEntity;

    inventory?: InventoryEntity;

    owner?: string;

    dutyLoadout?: {
        item: ItemTypeEntity;
        ammoEntity?: {
            name: string;
            multiplier: number;
        };
        components?: string[];
        storage: 'gun-rack' | 'trunk';
    }[];

    gurney?: {
        loaded: boolean;
        player?: PlayerMpLoggedIn;
    };

    nestedInventories?: InventoryEntity[];

    worldItems?: {
        item: ItemEntity;
        lock?: boolean;
        placementType: 'interior' | 'trunk';
        childInventory?: InventoryEntity;
    }[];

    lastInCar?: {
        driver?: CharacterEntity;
        occupant?: CharacterEntity;
        time?: number;
    };

    getEngineName() {
        const eng = vehicleSystem.engines.find((e) => e.level === this.engine_level);
        if (!eng) {
            return 'Unknown';
        }

        return eng.name;
    }

    getBrakesName() {
        const brk = vehicleSystem.brakes.find((e) => e.level === this.brake_level);
        if (!brk) {
            return 'Unknown';
        }

        return brk.name;
    }

    hasUpgrade(id: number) {
        return this.upgrades?.some((upgrade) => upgrade.id === id);
    }

    async loadOwner(): Promise<string> {
        if (this.character_id) {
            this.owner = (await getRepository(CharacterEntity).findOne({ id: this.character_id }))?.name;
        } else if (this.faction_id) {
            this.owner = (await getRepository(FactionEntity).findOne({ id: this.faction_id }))?.name;
        } else if (this.company_id) {
            this.owner = (await getRepository(CompanyEntity).findOne({ id: this.company_id }))?.name;
            const company = companySystem.getCompanyById(this.company_id);
            if (company) {
                company.vehicles.push(this);
            }
        }

        if (!this.owner) {
            this.owner = 'Unknown';
        }

        return this.owner;
    }

    getOwnerEntity(): CharacterEntity | CompanyEntity | FactionEntity | undefined {
        return this.character || this.company || this.faction;
    }
}
