package ru.alekseypleshkov.yandexmetrica

import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONArray
import org.json.JSONObject
import ru.alekseypleshkov.yandexmetrica.entity.Counter
import ru.alekseypleshkov.yandexmetrica.entity.Goal
import ru.alekseypleshkov.yandexmetrica.entity.Metrica
import java.lang.Thread.sleep
import java.util.*
import java.util.logging.Logger
import kotlin.concurrent.thread

/**
 * Project: yandex-metrica-kotlin
 * Author: Aleksey Pleshkov
 * Author website: http://alekseypleshkov.ru
 * Date: 19.12.16
 * Comment:
 */

class YandexMetrica(val configMetrica: ConfigMetrica) {

    // Логирование
    val log: Logger = Logger.getLogger(YandexMetrica::class.java.name)

    // Для работы с http
    private val client: OkHttpClient = OkHttpClient()

    // Список счетчиков в метрике
    private val counters: ArrayList<Counter> = ArrayList()


    //
    // Иницилизация данных
    //


    /**
     * Иницилизация всех данных.
     * Если в метрике много счетчиков, то может не сработать.
     * TODO: Добавить параметр таймаута для сбора данных.
     * @return YandexMetrica
     */
    fun initAllData(): YandexMetrica {
        initCounters()
        initMetrics()
        initGoals()
        return this
    }


    /**
     * Иницилизация только списка счетчиков, добавленных в метрику
     * @return YandexMetrica
     */
    fun initCounters(): YandexMetrica {
        log.info("Старт initCounters")

        counters.clear()
        val json: JSONObject = request(configMetrica.LINK_GET_COUNTERS)

        // Если в JSON ответе есть список счетчиков
        // То создаем экземпляр счетчика и записываем его в общий список
        if (json.has("counters")) {
            JSONArray(json.get("counters").toString())
                    .map { it as JSONObject }
                    .forEach {
                        val id = it.getLong("id")
                        val name = it.getString("name")
                        val site = it.getString("site")
                        val counter = Counter(id, name, site)
                        counters.add(counter)
                    }
        }

        log.info("Завершение initCounters")
        return this
    }


    /**
     * Иницилизация только одного счетчика
     * @return YandexMetrica
     */
    fun initSingleCounter(counterId: Long): YandexMetrica {
        log.info("Старт initSingleCounter")

        counters.clear()
        val linkStats = configMetrica.LINK_GET_SINGLE_COUNTER
                .replace("REPLACE_ID", counterId.toString())
        val json: JSONObject = request(linkStats)

        // Если в JSON ответе есть данные по нужному счетчику
        // То создаем экземпляр счетчика и записываем его в общий список
        if (json.has("counter")) {
            val dataCounter = json.getJSONObject("counter")
            val id = dataCounter.getLong("id")
            val name = dataCounter.getString("name")
            val site = dataCounter.getString("site")
            val counter = Counter(id, name, site)
            counters.add(counter)
        }

        log.info("Завершение initSingleCounter")
        return this
    }


    /**
     * Иницилизация данных о показах и визитах
     * Для работы данного метода, нужно изначально прогрузить счетчики initCounters()
     * @return YandexMetrica
     */
    fun initMetrics(): YandexMetrica {
        log.info("Старт initMetrics")

        // Запретить запуск метода, если счетчики не прогружены
        if (counters.count() == 0) {
            log.info("initMetrics - Нужно сначала запустить initCounters")
            return this
        }

        // Перебираем список счетчиков
        // И получаем статистику посещаемости по каждому счетчику
        for (counter in counters) {

            // Стандартная ссылка для получения статистики
            // Заменяем в ней некоторые данные на актуальные
            val linkStats = configMetrica.LINK_GET_STANDART_STATS
                    .replace("REPLACE_ID", counter.id.toString())
            val json: JSONObject = request(linkStats)

            // Проверка на существование данных
            if (json.has("totals")) {
                val totalsArray = json.getJSONArray("totals")
                if (totalsArray.count() == 4) {

                    // Получаем суммированные актуальные данные
                    val visits = totalsArray.getInt(0)
                    val pageViews = totalsArray.getInt(1)
                    val users = totalsArray.getInt(2)
                    var metrica = Metrica("total", "total", "none", visits, pageViews, users)
                    counter.metrics.add(metrica)

                    // Добавляем данные по группам (по рекламе, внутренние переходы..)
                    val dataArray = json.getJSONArray("data")
                    for (data in dataArray) {
                        val dataObject = data as JSONObject

                        // Получаем информацию о переходе и статистики этого источника
                        if (dataObject.has("dimensions") && dataObject.has("metrics")) {
                            val dimensions = dataObject.getJSONArray("dimensions")
                            val metrics = dataObject.getJSONArray("metrics")

                            if (dimensions.count() == 2 && metrics.count() == 4) {

                                // Разбито на два массива, получаем каждый для парсинга информации
                                val dimensionsArrayOne = dimensions.get(0) as JSONObject
                                val dimensionsArrayTwo = dimensions.get(1) as JSONObject

                                // ID и название перехода (реклама, поисковые системы..)
                                val id = dimensionsArrayOne.getString("id")
                                val name = "${dimensionsArrayOne.get("name")} | ${dimensionsArrayTwo.get("name")}"
                                // Переводим в тип String таким образом, т.к может быть null
                                val favicon = dimensionsArrayTwo.get("favicon").toString()

                                // Статистика этого источника
                                val visitsSource = metrics.getInt(0)
                                val pageViewsSource = metrics.getInt(1)
                                val usersSource = metrics.getInt(2)

                                // Проверяем, есть ли такая статистика уже в списке у счетчика
                                // Если есть, то добавляем данные к уже имеющимся
                                if (counter.hasMeticaId(id)) {
                                    counter.getMetricaFromId(id)?.let {
                                        it.visits += visitsSource
                                        it.pageViews += pageViewsSource
                                        it.users += usersSource
                                    }
                                } else {
                                    metrica = Metrica(id, name, favicon, visitsSource, pageViewsSource, usersSource)
                                    counter.metrics.add(metrica)
                                }

                            }
                        }
                    }

                }
            }
        }

        log.info("Завершение initMetrics")
        return this
    }


    /**
     * Иницилизация данных по целям
     * Для работы данного метода, нужно изначально прогрузить счетчики initCounters()
     * @return YandexMetrica
     */
    fun initGoals(): YandexMetrica {
        log.info("Старт initGoals")

        // Запретить запуск метода, если счетчики не прогружены
        if (counters.count() == 0) {
            log.info("initGoals - Нужно сначала запустить initCounters")
            return this
        }

        // Перебираем список счетчиков
        // И получаем список целей по каждому счетчику
        for (counter in counters) {
            counter.goals.clear()

            // Ссылка для получения списка целей
            // Заменяем в ней некоторые данные на актуальные
            val linkStats = configMetrica.LINK_GET_GOALS
                    .replace("REPLACE_ID", counter.id.toString())
            val json: JSONObject = request(linkStats)

            // Проверка на существование данных
            if (json.has("goals")) {
                val jsonData = json.getJSONArray("goals")
                JSONArray(jsonData.toString())
                        .map { it as JSONObject }
                        .forEach {
                            // Основные данные
                            val id = it.getLong("id")
                            val name = it.getString("name")
                            var type = ""
                            var url = ""
                            var reaches = 0
                            var percent: Double = 0.0

                            // Получаем тип цели и индитификатор
                            val conditions = it.getJSONArray("conditions")
                            for (condition in conditions) {
                                val cond = condition as JSONObject
                                if (cond.has("type") && cond.has("url")) {
                                    type = cond.getString("type")
                                    url = cond.getString("url")
                                }
                            }

                            // Формируем ссылку для получения данных по цели
                            val linkGoalStats = configMetrica.LINK_GET_GOAL_STATS
                                    .replace("REPLACE_ID", counter.id.toString())
                                    .replace("REPLACE_GOAL_ID", id.toString())
                            // Получаем данные о выполнении и конверсии цели
                            val jsonGoals: JSONObject = request(linkGoalStats)
                            if (jsonGoals.has("totals")) {
                                val goalsStats = jsonGoals.getJSONArray("totals")
                                if (goalsStats.count() == 2) {
                                    reaches = goalsStats.getInt(0)
                                    percent = goalsStats.getDouble(1)
                                }
                            }

                            // Записываем новую цель в список целей счетчика
                            val goal = Goal(id, name, type, url, reaches, percent)
                            counter.goals.add(goal)
                        }
            }
        }

        log.info("Завершение initGoals")
        return this
    }


    //
    // Возврат данных
    //


    /**
     * Возвращаем список счетчиков с их статистикой
     * @return ArrayList<Counter>
     */
    fun getCounters(): ArrayList<Counter> {
        return this.counters
    }


    /**
     * Возвращаем счетчик по его ID
     * @param id счетчика
     * @return Counter
     */
    fun getCounterFromId(id: Long): Counter? {
        return counters.firstOrNull { it.id == id }
    }


    //
    // Вспомогательные методы
    //


    /**
     * Запрос к странице сайта, с целью получения json.
     * @param url
     */
    fun request(url: String): JSONObject {
        val request = Request.Builder().url(url).build()
        val response = client.newCall(request).execute()
        val resultRequest = response.body().string()
        val result = JSONObject(resultRequest)
        return result
    }

}