import { PrismaClient } from "@prisma/client";
import {
	getCountryByCode,
	getCountryById,
	pixelsToLatLon,
} from "../utils/region.js";

export interface Region {
	id: number;
	cityId: number;
	name: string;
	number: number;
	countryId: number;
	flagId: number;
}

export class RegionService {
	constructor(private prisma: PrismaClient) {}

	// Ref: https://www.bigdatacloud.com/free-api/free-reverse-geocode-to-city-api
	async nominatim(lat: number, lon: number) {
		const response = await fetch(
			`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lon}&localityLanguage=en`
		);
		const data: any = await response.json();

		let city = data.locality || data.city || data.principalSubdivision;
		if (city) {
			city = city.replace(/Province/i, "").trim();
		}

		return {
			city,
			country: data.countryName,
			country_code: data.countryCode?.toLowerCase(),
		};
	}

	async resetAllRegionsData() {
		await this.prisma.tile.updateMany({
			data: { regionId: null },
		});
		await this.prisma.region.deleteMany();
		await this.prisma.city.deleteMany();
	}

	async getRegionForCoordinates(
		tileX: number,
		tileY: number,
		season: number = 0
	): Promise<Region> {
		// For debugging purposes only
		// await this.resetAllRegionsData();

		let tile = await this.prisma.tile.findFirst({
			where: { season, x: tileX, y: tileY },
			include: { region: { include: { city: true } } },
		});

		if (!tile) {
			tile = await this.prisma.tile.create({
				data: { season, x: tileX, y: tileY },
				include: { region: { include: { city: true } } },
			});
		}

		try {
			if (!tile.regionId) {
				const centerX = tile.x * 1000 + 500;
				const centerY = tile.y * 1000 + 500;
				const [lat, lon] = pixelsToLatLon(centerX, centerY);

				console.log(`Getting region for coordinates ${lat}, ${lon}.`);
				const nominatimData = await this.nominatim(lat, lon);
				if (!nominatimData.city)
					throw new Error(`City not found for coordinates ${lat}, ${lon}.`);

				const country = getCountryByCode(nominatimData.country_code);
				if (!country)
					throw new Error(
						`Country code ${nominatimData.country_code} not found.`
					);

				let city = await this.prisma.city.findFirst({
					where: { name: nominatimData.city },
				});

				if (!city) {
					city = await this.prisma.city.create({
						data: {
							name: nominatimData.city,
							countryId: country.id,
						},
					});
				}

				let region = await this.prisma.region.findFirst({
					where: { cityId: city.id, number: 1 },
				});

				if (!region) {
					region = await this.prisma.region.create({
						data: { cityId: city.id, number: 1 },
					});
				}

				tile = await this.prisma.tile.update({
					where: { id: tile.id },
					data: { regionId: region.id },
					include: { region: { include: { city: true } } },
				});
			}

			if (!tile.region) {
				throw new Error("Region not found for tile");
			}

			const country = getCountryById(tile.region.city.countryId);
			if (!country)
				throw new Error(`Country ID ${tile.region.city.countryId} not found.`);

			return {
				id: tile.region.id,
				cityId: tile.region.cityId,
				number: tile.region.number,
				name: tile.region.city.name,
				countryId: country.id,
				flagId: country.flag,
			};
		} catch (error) {
			console.error(error);

			const country = getCountryById(242);
			return {
				id: country!.id,
				cityId: 0,
				number: 0,
				name: country!.name,
				countryId: country!.id,
				flagId: country!.flag,
			};
		}
	}
}
