import { Reviews, Settings } from '@backend/domain'
import { ReviewsRepository, SettingsRepository } from '@backend/repository'
import { Injectable, Logger } from '@nestjs/common'
import ky from 'ky'
import { Raw } from 'typeorm'
import { GooglePlaceDetailsResponse, Review } from '../types'
import { GOOGLE } from '../../../config'
import { Cron } from '../../../base'

@Injectable()
export class FetchReviewsCron {
	private readonly logger = new Logger(FetchReviewsCron.name)

	private settingsMap: Map<string, Settings> = new Map()

	constructor(
		private readonly _reviewsRepository: ReviewsRepository,
		private readonly _settingsRepository: SettingsRepository,
	) {}

	@Cron({
		name: 'fetch_reviews',
		schedule: '0 0 2 * * *',
		appconfigScheduleKey: 'fetch_reviews_cron_schedule',
	})
	async fetchReviews() {
		await this.loadSettingsData()

		for (const [tenantId, settings] of this.settingsMap) {
			if (!settings.restaurant.googlePlaceId) {
				this.logger.warn({ message: `googlePlaceId is not set for ${tenantId}`, tenantId })
				continue
			}

			const placeDetails = await this.fetchPlaceDetails(settings.restaurant.googlePlaceId)

			const reviews = placeDetails.reviews.map(
				(review) =>
					({
						authorName: review.author_name,
						authorProfileImageUrl: review.profile_photo_url,
						rating: review.rating,
						text: review.text,
						timestamp: new Date(review.time * 1000),
					}) as Review,
			)

			const entity: Partial<Reviews> = {
				reviews,
				rating: placeDetails.rating,
				totalRatings: placeDetails.user_ratings_total,
				tenantId,
			}

			// eslint-disable-next-line unicorn-x/prefer-ternary
			if (await this._reviewsRepository.exist({ where: { tenantId } })) {
				await this._reviewsRepository.update({ tenantId }, entity)
			} else {
				await this._reviewsRepository.save(entity)
			}

			this.logger.log(`Reviews updated for tenant: ${tenantId}`)
		}
	}

	private async loadSettingsData(): Promise<void> {
		const settings = await this._settingsRepository.find({
			where: {
				reservation: Raw((alias) => `${alias} -> 'googleReservations' ->> 'enabled' = 'true'`),
			},
		})
		this.settingsMap = new Map(settings.map((setting) => [setting.tenantId as string, setting]))
	}

	private async fetchPlaceDetails(placeId: string): Promise<GooglePlaceDetailsResponse> {
		const response = await ky
			.get('https://maps.googleapis.com/maps/api/place/details/json', {
				searchParams: {
					placeid: placeId,
					key: GOOGLE.MAPS_API_KEY,
					fields: ['reviews', 'rating', 'user_ratings_total'].join(','),
					reviews_sort: 'newest',
				},
			})
			.json<{ result: GooglePlaceDetailsResponse }>()

		return response.result
	}
}
