package com.andr3h3nriqu3s.applications

import java.sql.ResultSet
import java.util.UUID
import org.springframework.http.MediaType
import org.springframework.http.HttpStatus
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.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
import org.springframework.web.server.ResponseStatusException

data class CreateFlair(
        var color: String,
        var name: String,
        var expr: String,
        var description: String?
)

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

    @PutMapping(path = ["/"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun create(
            @RequestBody flair: CreateFlair,
            @RequestHeader("token") token: String
    ): Flair {
        val user = sessionService.verifyTokenThrow(token)
        return flairService.createFlair(user, flair)
    }

    @PutMapping(path = ["/update"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun create(@RequestBody flair: Flair, @RequestHeader("token") token: String): Flair {
        val user = sessionService.verifyTokenThrow(token)

        var old_flair = flairService.getById(user, flair.id)
        if (old_flair == null) {
            throw NotFound()
        }

        if (old_flair.user_id != user.id) {
            throw NotAuth()
        }

        flair.user_id = old_flair.user_id

        return flairService.updateFlair(flair)
    }

    @GetMapping(path = ["/"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun list(@RequestHeader("token") token: String): List<Flair> {
        val user = sessionService.verifyTokenThrow(token)
        return flairService.listUser(user)
    }

    @GetMapping(path = ["/simple/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    public fun listSimple(@PathVariable id: String): List<SimpleFlair> {
        var application = applicationService.findApplicationByIdNoUser(id)
        if (application == null) {
            throw ResponseStatusException(HttpStatus.NOT_FOUND, "Application Not Found", null)
        }

        return flairService.listUserId(application.user_id).map { it.toFlairSimple() }
    }

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

data class SimpleFlair(
        val name: String,
        val description: String,
        val color: String,
)

data class Flair(
        var id: String,
        var user_id: String,
        var color: String,
        var name: String,
        var expr: String,
        var description: String,
) {
    companion object : RowMapper<Flair> {
        override public fun mapRow(rs: ResultSet, rowNum: Int): Flair {
            return Flair(
                    rs.getString("id"),
                    rs.getString("user_id"),
                    rs.getString("color"),
                    rs.getString("name"),
                    rs.getString("expr"),
                    rs.getString("description"),
            )
        }
    }

    fun toFlairSimple(): SimpleFlair {
        return SimpleFlair(this.name, this.description, this.color)
    }
}

data class FlairLink(var id: String, var application_id: String, var flair_id: String)

@Service
public class FlairService(val db: JdbcTemplate) {

    public fun linkFlair(application: Application, flair: Flair): FlairLink {
        val links =
                db.query(
                        "select * from flair_link where application_id=? and flair_id=?",
                        arrayOf(application.id, flair.id)
                ) { request, _ ->
                    FlairLink(
                            request.getString("id"),
                            request.getString("application_id"),
                            request.getString("flair_id")
                    )
                }

        if (links.size > 0) {
            return links[0]
        }

        val link = FlairLink(UUID.randomUUID().toString(), application.id, flair.id)

        db.update(
                "insert into flair_link (id, application_id, flair_id) values (?, ?, ?)",
                link.id,
                link.application_id,
                link.flair_id
        )

        return link
    }

    public fun unlinkFlair(applicationId: String, flairId: String) {
        db.update(
                "delete from flair_link where flair_id=? and application_id=?",
                flairId,
                applicationId,
        )
    }

    public fun listFromLinkApplicationId(id: String): List<Flair> =
            db.query(
                            "select f.id, f.user_id, f.color, f.name, f.expr, f.description from flair_link as fl inner join flair as f on f.id = fl.flair_id where application_id=? order by name asc;",
                            arrayOf(id),
                            Flair
                    )
                    .toList()

    public fun listUser(user: UserDb): List<Flair> =
            db.query(
                            "select * from flair where user_id=? order by name asc;",
                            arrayOf(user.id),
                            Flair
                    )
                    .toList()

    public fun listUserId(id: String): List<Flair> =
            db.query(
                            "select * from flair where user_id=? and description != '' order by name asc;",
                            arrayOf(id),
                            Flair
                    )
                    .toList()

    public fun getById(user: UserDb, id: String): Flair? {
        val items =
                db.query(
                                "select * from flair where user_id=? and id=?;",
                                arrayOf(user.id, id),
                                Flair
                        )
                        .toList()

        if (items.size == 0) {
            return null
        }
        return items[0]
    }

    public fun deleteById(user: UserDb, id: String): Flair {
        val flair = this.getById(user, id)
        if (flair == null) {
            throw NotFound()
        }

        db.update("delete from flair where id=?", id)

        return flair
    }

    public fun updateFlair(flair: Flair): Flair {
        db.update(
                "update flair set user_id=?, color=?, name=?, expr=?, description=? where id=?;",
                flair.user_id,
                flair.color,
                flair.name,
                flair.expr,
                flair.description,
                flair.id,
        )

        return flair
    }

    public fun createFlair(user: UserDb, flair: CreateFlair): Flair {
        val id = UUID.randomUUID().toString()

        var description = ""

        if (flair.description != null) {
            description = flair.description!!
        }

        var new_flair = Flair(id, user.id, flair.color, flair.name, flair.expr, description)

        db.update(
                "insert into flair (id, user_id, color, name, expr, description) values (?, ?, ?, ?, ?, ?)",
                new_flair.id,
                new_flair.user_id,
                new_flair.color,
                new_flair.name,
                new_flair.expr,
                new_flair.description
        )

        return new_flair
    }
}
