package ru.yandex.direct.web.entity.uac.controller

import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import java.beans.PropertyEditorSupport
import java.io.IOException
import java.time.LocalDate
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType.APPLICATION_JSON_VALUE
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.WebDataBinder
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.InitBinder
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import ru.yandex.direct.core.entity.feature.service.FeatureService
import ru.yandex.direct.core.entity.uac.grut.GrutTransactionProvider
import ru.yandex.direct.core.entity.uac.model.AdvType
import ru.yandex.direct.core.entity.uac.model.GroupByDateType
import ru.yandex.direct.core.entity.uac.repository.ydb.UacYdbUtils
import ru.yandex.direct.core.entity.uac.service.UacCampaignServiceHolder
import ru.yandex.direct.core.entity.uac.service.UacDbDefineService
import ru.yandex.direct.core.entity.user.model.User
import ru.yandex.direct.core.security.authorization.PreAuthorizeRead
import ru.yandex.direct.dbutil.model.ClientId
import ru.yandex.direct.feature.FeatureName
import ru.yandex.direct.grid.core.entity.campaign.service.GridCampaignService
import ru.yandex.direct.grid.core.entity.model.GdiOfferStats
import ru.yandex.direct.grid.core.entity.model.campaign.GdiCampaignStats
import ru.yandex.direct.grid.core.entity.offer.service.GridOfferService
import ru.yandex.direct.grid.processing.service.statistics.utils.CampaignContext
import ru.yandex.direct.grid.processing.service.statistics.utils.StatisticsUtils
import ru.yandex.direct.grid.processing.service.statistics.utils.UacCampaignContext
import ru.yandex.direct.web.core.security.DirectWebAuthenticationSource
import ru.yandex.direct.web.core.security.authentication.DirectCookieAuthProvider.PARAMETER_ULOGIN
import ru.yandex.direct.web.entity.uac.exportCampaignStatisticsFilename
import ru.yandex.direct.web.entity.uac.generateFileData
import ru.yandex.direct.web.entity.uac.getHeader
import ru.yandex.direct.web.entity.uac.getRowStats
import ru.yandex.direct.web.entity.uac.notFoundResponse
import ru.yandex.direct.web.entity.uac.notOwnerResponse
import ru.yandex.direct.web.entity.uac.toReportOptionDate

@RestController
@Api(tags = ["uac"])
@RequestMapping("/uac", produces = [APPLICATION_JSON_VALUE])
open class UacStatisticsController(
    private val authenticationSource: DirectWebAuthenticationSource,
    private val serviceHolder: UacCampaignServiceHolder,
    private val gridCampaignService: GridCampaignService,
    private val gridOfferService: GridOfferService,
    private val grutTransactionProvider: GrutTransactionProvider,
    private val uacDbDefineService: UacDbDefineService,
    private val featureService: FeatureService
) {
    companion object {
        private const val PARAMETER_GROUP_BY_DATE = "groupByDate"
        private val logger = LoggerFactory.getLogger(UacStatisticsController::class.java)
    }

    @ApiOperation(
        value = "exportStatistics",
        httpMethod = "GET",
        nickname = "exportStatistics",
    )
    @PreAuthorizeRead
    @GetMapping(value = ["campaign/{id}/export_statistics"])
    open fun exportCampaignStatistics(
        @PathVariable id: Long,
        @RequestParam(value = PARAMETER_ULOGIN) ulogin: String?,
        @RequestParam(value = PARAMETER_GROUP_BY_DATE) groupByDate: GroupByDateType,
        @RequestParam(value = "from") dateFrom: Long,
        @RequestParam(value = "to") dateTo: Long,
    ): ResponseEntity<Any> {
        val subjectUser = authenticationSource.authentication.subjectUser
        val operator = authenticationSource.authentication.operator

        val useGrut = uacDbDefineService.useGrutForDirectCampaignId(id)

        return grutTransactionProvider.runInTransactionIfNeeded(useGrut) {
            getCampaignStatisticsExportImpl(
                useGrut,
                id,
                operator,
                subjectUser.clientId,
                subjectUser.uid,
                groupByDate,
                dateFrom,
                dateTo,
            )
        }
    }

    private fun getCampaignStatisticsExportImpl(
        useGrut: Boolean,
        id: Long,
        operator: User,
        clientId: ClientId,
        clientUid: Long,
        groupByDate: GroupByDateType,
        dateFrom: Long,
        dateTo: Long
    ): ResponseEntity<Any> {
        val uacCampaignService = serviceHolder.getUacCampaignService(useGrut)
        val uacCampaignId = uacCampaignService.getCampaignIdByDirectCampaignId(id) ?: return notFoundResponse()
        val uacCampaign = uacCampaignService.getCampaignById(uacCampaignId) ?: return notFoundResponse()

        if (!uacCampaignService.checkVisibleCampaign(operator.uid, clientId, id)) {
            return notOwnerResponse()
        }

        if (!(uacCampaign.advType == AdvType.MOBILE_CONTENT || uacCampaign.advType == AdvType.TEXT)) {
            logger.error("Campaign type is not uac/uc, type is ${uacCampaign.advType}")
            return notFoundResponse()
        }

        val startDate = UacYdbUtils.fromEpochSecond(dateFrom).toLocalDate()
        val endDate = UacYdbUtils.fromEpochSecond(dateTo).toLocalDate()

        val rmpCampaignContext = UacCampaignContext(uacCampaign)

        val getRevenueOnlyByAvailableGoals: Boolean =
            featureService.isEnabled(clientUid, FeatureName.GET_REVENUE_ONLY_BY_AVAILABLE_GOALS)
        val getCampaignStatsOnlyByAvailableGoals: Boolean =
            featureService.isEnabled(clientUid, FeatureName.GRID_CAMPAIGN_GOALS_FILTRATION_FOR_STAT)
        val isUacWithOnlyInstallEnabled =
            featureService.isEnabled(clientUid, FeatureName.ENABLE_UAC_WITH_ONLY_INSTALL)

        val goalIds = rmpCampaignContext.goalIds
        val stats: Map<LocalDate, GdiCampaignStats> = gridCampaignService.getCampaignStatsGroupByDate(
            listOf(id),
            startDate,
            endDate,
            goalIds,
            null,
            toReportOptionDate(groupByDate),
            getRevenueOnlyByAvailableGoals,
            getCampaignStatsOnlyByAvailableGoals,
            isUacWithOnlyInstallEnabled,
        )[id]!!
        val offerStats: Map<LocalDate, GdiOfferStats> = gridOfferService.getOfferStatsByDateByCampaignId(
            clientId,
            listOf(id),
            startDate,
            endDate,
            toReportOptionDate(groupByDate)
        )[id] ?: emptyMap()

        val result = StatisticsUtils.getYtStatsForUac(stats, offerStats, CampaignContext(rmpCampaignContext))

        val intro = arrayOf(
            "${uacCampaign.name} \n($uacCampaignId) \n$startDate - $endDate"
        )
        val rowsetHeaders: List<String> = getHeader()

        val rowset = result
            .map { getRowStats(it.columnValues, it.date) }

        val fileName = exportCampaignStatisticsFilename(uacCampaignId, startDate, endDate, groupByDate)
        val file = try {
            generateFileData(intro, rowsetHeaders, rowset)
        } catch (e: IOException) {
            logger.error("Failed to write statistics to csv", e)
            return ResponseEntity(
                HttpStatus.INTERNAL_SERVER_ERROR
            )
        }

        return ResponseEntity.status(HttpStatus.OK)
            .header("Content-Disposition", "attachment; filename=$fileName")
            .contentType(org.springframework.http.MediaType.parseMediaType("text/csv"))
            .body(file.toByteArray())
    }

    @InitBinder
    fun initBinder(dataBinder: WebDataBinder) {
        dataBinder.registerCustomEditor(GroupByDateType::class.java, GroupByDateTypeConverter())
    }

    inner class GroupByDateTypeConverter : PropertyEditorSupport() {
        override fun setAsText(text: String?) {
            value = text?.let { GroupByDateType.fromText(it.lowercase()) }
        }
    }
}
