import { getRepository } from 'typeorm';
import { FeatureSystem } from '../../core/feature-system';
import { playerRegistry, PlayerWrapper } from '../../core/player/player-registry';
import { PublicSpawnEntity } from '../../entities/public-spawn.entity';
import { LogSystem } from '../../core/log';
import { ChatSystem } from '../chat';
import { propertySystem } from '../properties';

const DEFAULT_SPAWN = { x: -1037.43, y: -2733.075, z: 13.7566 };

export class PlayerSystem extends FeatureSystem {
    publicSpawns: PublicSpawnEntity[] = [];

    async init(): Promise<void> {
        this.publicSpawns = await getRepository(PublicSpawnEntity).find();
        super.init(`Loaded ${this.publicSpawns.length} public spawns`);
    }

    async spawn(player: PlayerMpLoggedIn, _firstSpawn = false): Promise<void> {
        const ch = player.character;

        if (ch.killed) {
            player.spawn({ x: 2513, y: -429, z: 114 });
            LogSystem.PlayerEvent(player, 'spawned', 'Spawned (character killed)');
            return;
        }

        if (!ch.spawn) {
            // No spawn set — login system will have sent login:spawns to client; nothing to do here
            return;
        }

        const parts = ch.spawn.split(':');

        if (parts[0] === 'public') {
            const housing = this.publicSpawns.find((s) => s.id === Number(parts[1]));
            if (!housing) {
                ChatSystem.Notice(player, 'Your public spawn no longer exists. Spawning at default.');
                player.spawn(DEFAULT_SPAWN);
                LogSystem.PlayerEvent(player, 'spawned', 'Spawned (default — public spawn missing)');
                return;
            }
            player.spawn({ x: housing.x, y: housing.y, z: housing.z });
            LogSystem.PlayerEvent(player, 'spawned', `Spawned (public spawn ${housing.id})`);
            return;
        }

        if (parts[0] === 'property') {
            const property = propertySystem.properties.find((p) => p.id === Number(parts[1]));
            if (!property || !property.interior_x) {
                ChatSystem.Notice(player, 'Your property spawn is no longer available. Spawning at default.');
                player.spawn(DEFAULT_SPAWN);
                LogSystem.PlayerEvent(player, 'spawned', 'Spawned (default — property spawn missing)');
                return;
            }
            player.spawn({ x: property.interior_x, y: property.interior_y!, z: property.interior_z! });
            LogSystem.PlayerEvent(player, 'spawned', `Spawned (property ${property.id})`);
            return;
        }

        if (parts[0] === 'coords') {
            const [cx, cy, cz] = parts[1].split(',').map(Number);
            if (isFinite(cx) && isFinite(cy) && isFinite(cz)) {
                player.spawn({ x: cx, y: cy, z: cz });
                LogSystem.PlayerEvent(player, 'spawned', 'Spawned (saved coords)');
                return;
            }
        }

        player.spawn(DEFAULT_SPAWN);
        LogSystem.PlayerEvent(player, 'spawned', 'Spawned (default)');
    }

    static GetPlayerFromString(
        str: string,
        byCharacter: boolean,
        byAccount: boolean,
        byId: boolean,
        _exact = true,
        _includeAdminDuty = false,
    ): PlayerMpLoggedIn | null {
        const id = parseInt(str, 10);
        for (const player of playerRegistry.toArray()) {
            if (!player.character || !player.account) continue;
            if (byId && !isNaN(id) && player.id === id) return player as PlayerMpLoggedIn;
            if (byCharacter && player.character.name?.toLowerCase().startsWith(str.toLowerCase())) return player as PlayerMpLoggedIn;
            if (byAccount && player.account.name?.toLowerCase().startsWith(str.toLowerCase())) return player as PlayerMpLoggedIn;
        }
        return null;
    }

    static GetPlayerFromCharacterId(characterId: number): PlayerMpLoggedIn | undefined {
        return playerRegistry.toArray().find((p) => p.character?.id === characterId) as PlayerMpLoggedIn | undefined;
    }

    static GetPlayerFromAccountId(accountId: number): PlayerMpLoggedIn | undefined {
        return playerRegistry.toArray().find((p) => p.account?.id === accountId) as PlayerMpLoggedIn | undefined;
    }

    static IsPremium(_player: PlayerMp): boolean { return false; }

    async getCharacterFromId(_id: number) { return undefined; }
    async getCharacterFromIdWithBP(_id: number) { return undefined; }

    getNameWithAccountName(player: PlayerMpLoggedIn): string {
        return `${player.character.name} (${player.account.name})`;
    }
}

export const playerSystem = new PlayerSystem();
