/* Copyright 2018 Conny Duck
 *
 * This file is a part of lecjoa.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * lecjoa is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with lecjoa; if not,
 * see <http://www.gnu.org/licenses>. */

package net.letime.lecjoa

import android.content.Intent
import android.os.Bundle
import android.support.annotation.VisibleForTesting
import android.support.design.widget.BottomSheetBehavior
import android.view.View
import android.widget.LinearLayout
import net.letime.lecjoa.entity.SearchResults
import net.letime.lecjoa.entity.Status
import net.letime.lecjoa.network.MastodonApi
import net.letime.lecjoa.util.LinkHelper
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.net.URI
import java.net.URISyntaxException
import javax.inject.Inject

/** this is the base class for all activities that open links
 *  links are checked against the api if they are mastodon links so they can be openend in lecjoa
 *  Subclasses must have a bottom sheet with Id item_status_bottom_sheet in their layout hierachy
 */

abstract class BottomSheetActivity : BaseActivity() {

    lateinit var bottomSheet: BottomSheetBehavior<LinearLayout>
    var searchUrl: String? = null

    @Inject
    lateinit var mastodonApi: MastodonApi

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)

        val bottomSheetLayout: LinearLayout = findViewById(R.id.item_status_bottom_sheet)
            bottomSheet = BottomSheetBehavior.from(bottomSheetLayout)
            bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
            bottomSheet.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                override fun onStateChanged(bottomSheet: View, newState: Int) {
                   if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                        cancelActiveSearch()
                    }
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {}
            })

    }

    open fun viewUrl(url: String) {
        if (!looksLikeMastodonUrl(url)) {
            openLink(url)
            return
        }

        val call = mastodonApi.search(url, true)
        call.enqueue(object : Callback<SearchResults> {
            override fun onResponse(call: Call<SearchResults>, response: Response<SearchResults>) {
                if (getCancelSearchRequested(url)) {
                    return
                }

                onEndSearch(url)
                if (response.isSuccessful) {
                    // According to the mastodon API doc, if the search query is a url,
                    // only exact matches for statuses or accounts are returned
                    // which is good, because pleroma returns a different url
                    // than the public post link
                    val searchResult = response.body()
                    if(searchResult != null) {
                        if (searchResult.statuses.isNotEmpty()) {
                            viewThread(searchResult.statuses[0])
                            return
                        } else if (searchResult.accounts.isNotEmpty()) {
                            viewAccount(searchResult.accounts[0].id)
                            return
                        }
                    }
                }
                openLink(url)
            }

            override fun onFailure(call: Call<SearchResults>, t: Throwable) {
                if (!getCancelSearchRequested(url)) {
                    onEndSearch(url)
                    openLink(url)
                }
            }
        })
        callList.add(call)
        onBeginSearch(url)
    }

    open fun viewThread(status: Status) {
        if (!isSearching()) {
            val intent = Intent(this, ViewThreadActivity::class.java)
            intent.putExtra("id", status.actionableId)
            intent.putExtra("url", status.actionableStatus.url)
            startActivity(intent)
        }
    }

    open fun viewAccount(id: String) {
        val intent = AccountActivity.getIntent(this, id)
        startActivity(intent)
    }

    @VisibleForTesting
    fun onBeginSearch(url: String) {
        searchUrl = url
        showQuerySheet()
    }

    @VisibleForTesting
    fun getCancelSearchRequested(url: String): Boolean {
        return url != searchUrl
    }

    @VisibleForTesting
    fun isSearching(): Boolean {
        return searchUrl != null
    }

    @VisibleForTesting
    fun onEndSearch(url: String?) {
        if (url == searchUrl) {
            // Don't clear query if there's no match,
            // since we might just now be getting the response for a canceled search
            searchUrl = null
            hideQuerySheet()
        }
    }

    @VisibleForTesting
    fun cancelActiveSearch() {
        if (isSearching()) {
            onEndSearch(searchUrl)
        }
    }

    @VisibleForTesting
    open fun openLink(url: String) {
        LinkHelper.openLink(url, this)
    }

    private fun showQuerySheet() {
            bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
    }

    private fun hideQuerySheet() {
            bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
    }
}

// https://mastodon.foo.bar/@User
// https://mastodon.foo.bar/@User/43456787654678
// https://pleroma.foo.bar/users/User
// https://pleroma.foo.bar/users/43456787654678
// https://pleroma.foo.bar/notice/43456787654678
// https://pleroma.foo.bar/objects/d4643c42-3ae0-4b73-b8b0-c725f5819207
fun looksLikeMastodonUrl(urlString: String): Boolean {
    val uri: URI
    try {
        uri = URI(urlString)
    } catch (e: URISyntaxException) {
        return false
    }

    if (uri.query != null ||
            uri.fragment != null ||
            uri.path == null) {
        return false
    }

    val path = uri.path
    return path.matches("^/@[^/]+$".toRegex()) ||
            path.matches("^/users/[^/]+$".toRegex()) ||
            path.matches("^/@[^/]+/\\d+$".toRegex()) ||
            path.matches("^/notice/\\d+$".toRegex()) ||
            path.matches("^/objects/[-a-f0-9]+$".toRegex())
}
