package com.andr3h3nriqu3s.applications

import java.sql.ResultSet
import java.util.UUID
import kotlin.collections.setOf
import okhttp3.OkHttpClient
import okhttp3.Request
import org.springframework.http.MediaType
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.core.RowMapper
import org.springframework.stereotype.Service
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

data class Application(
        var id: String,
        var url: String,
        var original_url: String?,
        var unique_url: String?,
        var title: String,
        var user_id: String,
        var extra_data: String,
        var payrange: String,
        var status: Int,
        var flairs: List<Flair>
) {
    companion object : RowMapper<Application> {
        override public fun mapRow(rs: ResultSet, rowNum: Int): Application {
            return Application(
                    rs.getString("id"),
                    rs.getString("url"),
                    rs.getString("original_url"),
                    rs.getString("unique_url"),
                    rs.getString("title"),
                    rs.getString("user_id"),
                    rs.getString("extra_data"),
                    rs.getString("payrange"),
                    rs.getInt("status"),
                    emptyList()
            )
        }
    }
}

data class SubmitRequest(val text: String)

data class ListRequest(val status: Int?)

data class StatusRequest(val id: String, val status: Int)

data class FlairRequest(val id: String, val text: String)

data class UpdateUrl(val id: String, val url: String)

@RestController
@ControllerAdvice
@RequestMapping("/api/application")
class ApplicationsController(
        val sessionService: SessionService,
        val applicationService: ApplicationService,
        val flairService: FlairService
) {

    @PostMapping(path = ["/text"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun submitText(
            @RequestBody submit: SubmitRequest,
            @RequestHeader("token") token: String
    ): Int {
        val user = sessionService.verifyTokenThrow(token)

        var text = submit.text.replace("=\n", "")

        var urls: List<String> = emptyList()

        while (true) {
            var index = text.indexOf("href")

            if (index == -1) {
                break
            }

            var new_url = StringBuilder()

            var found_start = false

            while (true) {
                if (found_start) {
                    if (text[index] == '"') {
                        break
                    }
                    new_url.append(text[index])
                } else if (text[index] == '"') {
                    found_start = true
                }
                index++
            }

            text = text.substring(index)

            urls = urls.plus(new_url.toString().replace("&amp;", "&").replace("=3D", "="))
        }

        print("found: ")
        print(urls.size)
        print(" links\n")

        urls = urls.filter { predicate -> predicate.contains("jobListing") }

        print("found fileted: ")
        print(urls.size)
        print(" links\n")

        urls = urls.toSet().toList()

        print("removed duplicates: ")
        print(urls.size)
        print(" links\n")

        var client = OkHttpClient()

        var applications =
                urls.map { elm ->
                    var request =
                            Request.Builder()
                                    .url(elm)
                                    .header(
                                            "User-Agent",
                                            "Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0"
                                    )
                                    .addHeader(
                                            "Accept",
                                            "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
                                    )
                                    .addHeader(
                                            "Cookie",
                                            "gdId=aafdcb86-c37d-4bfd-8d84-4a07f11dbfc2; AWSALB=Qj3Rvldo90lFQhsChPHFXcGlVxdaZOEwr1ZP4d+whxBTlXCprKj+utUCz4DHXoC6fFrewDdFdRCPBbM0v2WQQdY4URUD4lYXd0GsBEWS/5AzZX0i9yk4C7Cfz5xA; AWSALBCORS=Qj3Rvldo90lFQhsChPHFXcGlVxdaZOEwr1ZP4d+whxBTlXCprKj+utUCz4DHXoC6fFrewDdFdRCPBbM0v2WQQdY4URUD4lYXd0GsBEWS/5AzZX0i9yk4C7Cfz5xA; indeedCtk=1i17t757dk2mo801; at=Sf_nQa0d6HxrGoYn305KELOfsWT0o-KrKdraenJrTJMqApEH4LO70x3i0oOafGWes13u8kOSq8PzPD1pZAxAVOEz0hrNnOhRnLGl3Ako4I-CYx_oCB_YhFe7M-do__yNC-ipCVlbzUujwQzCW83mKFLMHk9N12RSjhzsPEQi6Xw4n66wvXSttDChGCpY-BMSn862U-OQ9gnNzy3j214vctizTPC3g2Ig7Qysg3AY7lo-HPkHaq9fojbituLfbtgtpkCDtWAwR2-3XCWu6d2H_gk7ZWn6CH1FgLf640fd1Kep7K3kArceNq046PEalzHnJMLPSnvusS_jJatdk-x4k2rxF1FJvK0MLkPiKr39V8i7J27uOMwFLRD-49EoJUOZzTxQ1wb3lyt503NuwPhYolRGr3Xla2b6wyzYRSk2e-RHHElzvCn-FlWXaTQqEiZXKEjAGihlagUz8HFmY6bRf0FLCKvA26oVjT1vEoICmn32FOHr9M65YDllTbgHj3dDeTUczzxf_nrDSyide_UL08LsZIYJAtyueUSBqvqq49WlKemvhyKBMUloNP68A_MfT5suKPasvQu-n_ZvXahY7O5KbgtIR0QVnSimFC8yaVy8Sqjo5cq2f1VnZ6kxXj9frSK-v_jz1J7hobOAo8Mlfy_EfzBDV_3nywqjT1pKgzRRmHcNLnj_cn2rPga3NUjxpYzl485eevPDv9Gj62qAMHh1w5S6v1QkSBmgaSyKpBKYD1zD86JLJ6Upf0bVM8WcHv9u33GuKX0CgC_P-P3189AgLgtISr-2NuIaNTkKMxenagCvri6oGLj6OYMD6nO9JQ_jKUqS6ShRMhDD9K1bSUE42c2_ePhBDLWFyb3AZiJvVjcL1v1k1dO93jCn7XUQZkE7x7m1QeGC0f_5OK1IZeGuJn8Jsx-u2EvT7DNSV0LNY1hfIXauy9KCLFdTeoqDuGSumi81cg; uc=8013A8318C98C5171B57B9070099BAD115C8BBA5C5A6DB94DFFE7C86F37B699E2B54A4621F700A694011D5B5CA447CCC87B1BAC35D51A1AA5B5FF13CBDC5343400624A80C8E3C3A1966ED67EBC62D473CD7C57C9E6D138AD745B36C8625AD88EA13CE3458B51AD202B52198961B4D49007278A9385005B27B951F0F7091D61DCBBD54D21C8CFBCAD7AABEF4EA398F978; trs=direct:direct:direct:2024-06-25+07%3A59%3A23.686:undefined:undefined; cf_clearance=IZpDoUoDUUNidzYbSZRoIq6S93FXgL7hmz6.PdfEiUM-1720205316-1.0.1.1-tNL38ZNHbQw9XbL3J9g4oL4aHwiAoXXuYdOb4Xi8Z3B4yWKOuL31FrrPrqPRcxVUV7BNEdlqSMaNF3k8q.Tv0A; rl_session=RudderEncrypt%3AU2FsdGVkX19OL1VCjdlEuacjPBxOQ%2FsqFNPp5d9Dke5bKES7onFGAEFzkA4iuW3rfqQ8v2sfoX19gn7Zm3Qd83i6PWHRHESeelsza78DN%2B2U7IwWQEiyEeptsuZyTPhHxb5ALLCzUhgHcvnsh3GhQw%3D%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX19%2BQEGQCkE9Cg3Ikh5t8h65RqO0N24wvgA%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2B1EoAGj4RTTyGWbZRPkyuua9oOW4YQPaE%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX18STRtzoe4K%2FzJKt8zfNqQwMj7KTzQDhfQ%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX1%2B5Kbrex9bI6O0Gr524R6IRASVZGgrWNco%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19X88n4GXRAtkOTDXcuHojO%2BetO8p7n37y2YLe6nWW5P87Stu9l86nJ4zGDs7e8D5MEUPW6KrCywQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19MgsL8FFX%2BSjRpwcg0nu4XxNDvIPdg2WGEivR2%2BsH1%2BqAvvlaEZmiQ; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18kgmzvlkMRtIo%2BzqUQ%2FVRfcuCvngKkmV8qAgqKAKraZOr9Qpqc6loH; _cfuvid=ezdGXFBaF3Eh.jiKZrHkmaV7GItIbZ4qPUy7gOyHgns-1720604124536-0.0.1.1-604800000; gdGPCset=true; _urc=293647244; AFSID=MTQ3YTQxNzEtZmYwMC00N2Y0LWJiOGMtNzVjMmRjZWM5MTQ4; gdsid=1720604123405:1720637554591:4466F8BAFD3D2D34B6058423B0F90392; JSESSIONID=BA9AA08792C5E981A69CF079E7A91B35; GSESSIONID=undefined; cass=0; asst=1720637634.1; __cf_bm=6b1QlZvq2zEE.Cqvcg0g2a0OiPObgL0IKFr.vZwZSwQ-1720637555-1.0.1.1-qaRfjwA.xRIpqzfJiksMvc_hWVvVnfGJ1YnRToNwK.m3Od0BXKUvOVnvhrpiR9bP311eCdI.EAYQZvKpwD7KHQ; bs=oiDddWzpeaDeES0KI7rnsQ:NDp0g4i9mXeF0vvvvYq2Ud_auNQosyTqd2NVCprviXG0nOGhPzqbVJa16DxpiFQkIluzbbDJqHLht66Dwrqn4yi7u_FvaGOojP_y91QEUZU:Hex7BRo2zXhuIdlGhiQR8uO5nGn2VCjpx6dF2aJPIxk"
                                    )
                                    .addHeader("Accept-Language", "en-US,en;q=0.7,pt;q=0.3")
                                    .addHeader("Accept-Encoding", "gzip, deflate, br, zstd")
                                    .addHeader("Connection", "keep-alive")
                                    .build()

                    var new_url: String =
                            client.newCall(request).execute().use { response ->
                                print("response:")
                                print(response)
                                print("\n")
                                response.request.url.toString()
                            }

                    var unique: String? = new_url!!.split("?")[0]

                    var original_url: String? = elm

                    if (new_url == elm) {
                        original_url = null
                        unique = null
                    }

                    Thread.sleep(2000)

                    print("Got new url!\n")

                    Application(
                            UUID.randomUUID().toString(),
                            new_url,
                            original_url,
                            unique,
                            "New Aplication",
                            user.id,
                            "",
                            "",
                            0,
                            emptyList()
                    )
                }

        applications =
                applications.filter { elm -> applicationService.createApplication(user, elm) }

        print("created new: ")
        print(applications.size)
        print(" links\n")

        return applications.size
    }

    @PostMapping(path = ["/text/flair"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun textFlair(
            @RequestBody info: FlairRequest,
            @RequestHeader("token") token: String
    ): Int {
        val user = sessionService.verifyTokenThrow(token)
        val application = applicationService.findApplicationById(user, info.id)
        if (application == null) {
            throw NotFound()
        }

        val flairs = flairService.listUser(user)

        var count = 0

        for (flair: Flair in flairs) {
            val regex =
                    Regex(
                            ".*" + flair.expr + ".*",
                            setOf(RegexOption.IGNORE_CASE, RegexOption.DOT_MATCHES_ALL)
                    )

            if (regex.matches(info.text)) {
                count += 1
                flairService.linkFlair(application, flair)
            }
        }

        return count
    }

    @PostMapping(path = ["/list"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun list(
            @RequestBody info: ListRequest,
            @RequestHeader("token") token: String
    ): List<Application> {
        val user = sessionService.verifyTokenThrow(token)
        return applicationService.findAll(user, info)
    }

    @GetMapping(path = ["/active"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun active(@RequestHeader("token") token: String): Application? {
        val user = sessionService.verifyTokenThrow(token)
        val possibleApplications = applicationService.findAll(user, ListRequest(1))
        if (possibleApplications.size == 0) {
            return null
        }
        return applicationService.findApplicationById(user, possibleApplications[0].id)
    }

    @PutMapping(path = ["/status"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun status(
            @RequestBody info: StatusRequest,
            @RequestHeader("token") token: String,
    ): Application {
        val user = sessionService.verifyTokenThrow(token)
        var application = applicationService.findApplicationById(user, info.id)
        if (application == null) {
            throw NotFound()
        }

        application.status = info.status

        applicationService.update(application)

        return application
    }

    @PutMapping(path = ["/update"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun update(
            @RequestBody info: Application,
            @RequestHeader("token") token: String
    ): Application {
        val user = sessionService.verifyTokenThrow(token)
        var application = applicationService.findApplicationById(user, info.id)
        if (application == null) {
            throw NotFound()
        }
        applicationService.update(info)
        return info
    }

    @PostMapping(path = ["/update/url"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun updateUrl(
            @RequestBody info: UpdateUrl,
            @RequestHeader("token") token: String
    ): Application {
        val user = sessionService.verifyTokenThrow(token)
        var application = applicationService.findApplicationById(user, info.id)

        if (application == null) {
            throw NotFound()
        }

        if (application.unique_url != null) {
            throw BadRequest()
        }

        application.original_url = application.url
        application.url = info.url
        application.unique_url = info.url.split("?")[0]

        var maybe_exists =
                applicationService.findApplicationByUrl(
                        user,
                        application.url,
                        application.unique_url
                )
        if (maybe_exists != null) {
            applicationService.delete(application)

            if (maybe_exists.status == 0 && application.status == 1) {
                maybe_exists.status = 1
                applicationService.update(maybe_exists)
            }

            maybe_exists.flairs = flairService.listFromLinkApplicationId(maybe_exists.id)

            return maybe_exists
        }

        applicationService.update(application)

        application.flairs = flairService.listFromLinkApplicationId(application.id)

        return application
    }

    @PostMapping(path = ["/reset/url/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun updateUrl(
            @PathVariable id: String,
            @RequestHeader("token") token: String
    ): Application {
        val user = sessionService.verifyTokenThrow(token)
        var application = applicationService.findApplicationById(user, id)

        if (application == null) {
            throw NotFound()
        }

        if (application.unique_url == null) {
            throw BadRequest()
        }

        application.url = application.original_url!!
        application.original_url = null
        application.unique_url = null

        applicationService.update(application)

        application.flairs = flairService.listFromLinkApplicationId(application.id)

        return application
    }

    @DeleteMapping(path = ["/flair/{id}/{flairid}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun delete(
            @PathVariable id: String,
            @PathVariable flairid: String,
            @RequestHeader("token") token: String
    ): Application {
        val user = sessionService.verifyTokenThrow(token)

        val application = applicationService.findApplicationById(user, id)
        if (application == null) {
            throw NotFound()
        }

        flairService.unlinkFlair(id, flairid)
        return applicationService.findApplicationById(user, id)!!
    }

    @DeleteMapping(path = ["/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun delete(
            @PathVariable id: String,
            @RequestHeader("token") token: String
    ): Application {
        val user = sessionService.verifyTokenThrow(token)

        val application = applicationService.findApplicationById(user, id)
        if (application == null) {
            throw NotFound()
        }

        applicationService.delete(application)
        return application
    }
}

@Service
class ApplicationService(val db: JdbcTemplate, val flairService: FlairService) {

    public fun findApplicationByUrl(user: UserDb, url: String, unique_url: String?): Application? {
        if (unique_url != null) {
            val unique =
                    db.query(
                                    "select * from applications where unique_url=? and user_id=?",
                                    arrayOf(unique_url, user.id),
                                    Application
                            )
                            .toList()

            if (unique.size != 0) {
                return unique[0]
            }
        }

        val applications =
                db.query(
                                "select * from applications where url=? and user_id=?",
                                arrayOf(url, user.id),
                                Application
                        )
                        .toList()

        if (applications.size == 0) {
            return null
        }

        return applications[0]
    }

    public fun findApplicationById(user: UserDb, id: String): Application? {
        var applications =
                db.query(
                                "select * from applications where id=? and user_id=?",
                                arrayOf(id, user.id),
                                Application
                        )
                        .toList()

        if (applications.size == 0) {
            return null
        }

        var application = applications[0]

        application.flairs = flairService.listFromLinkApplicationId(application.id)

        return application
    }

    public fun createApplication(user: UserDb, application: Application): Boolean {
        if (this.findApplicationByUrl(user, application.url, application.unique_url) != null) {
            return false
        }

        db.update(
                "insert into applications (id, url, original_url, unique_url, title, user_id, extra_data, payrange, status) values (?, ?, ?, ?, ?, ?, ?, ?, ?);",
                application.id,
                application.url,
                application.original_url,
                application.unique_url,
                application.title,
                application.user_id,
                application.extra_data,
                application.payrange,
                application.status,
        )

        return true
    }

    public fun findAll(user: UserDb, info: ListRequest): List<Application> {
        if (info.status == null) {
            return db.query(
                            "select * from applications where user_id=? order by title asc;",
                            arrayOf(user.id),
                            Application
                    )
                    .toList()
        }

        return db.query(
                        "select * from applications where user_id=? and status=? order by title asc;",
                        arrayOf(user.id, info.status),
                        Application,
                )
                .toList()
    }

    public fun update(application: Application): Application {
        db.update(
                "update applications set url=?, original_url=?, unique_url=?, title=?, user_id=?, extra_data=?, payrange=?, status=? where id=?",
                application.url,
                application.original_url,
                application.unique_url,
                application.title,
                application.user_id,
                application.extra_data,
                application.payrange,
                application.status,
                application.id,
        )
        return application
    }

    public fun delete(application: Application) {
        db.update(
                "delete from applications where id=?",
                application.id,
        )
    }
}
