use crate::permissions::Perm;
use crate::repository::RepoPath;
use crate::Config;
use axum::{
    extract::{Form, State},
    response::{IntoResponse, Redirect, Response},
    routing::post,
    Router,
};
use axum_extra::extract::SignedCookieJar;
use serde_derive::*;
use tracing::*;

pub fn router() -> Router<Config> {
    Router::new()
        .route("/{owner}/{repo}/fork", post(fork))
        .route("/{owner}/{repo}/prune", post(prune))
        .route("/{owner}/{repo}/rename", post(rename))
        .fallback(crate::fallback)
}

pub const MAIN_CHANNEL: &'static str = "main";

#[derive(Debug, Deserialize)]
pub struct Fork {
    name: String,
    token: String,
}

async fn fork(
    State(config): State<crate::Config>,
    jar: SignedCookieJar,
    p: RepoPath,
    token: axum_csrf::CsrfToken,
    Form(fork): Form<Fork>,
) -> Result<Response, crate::Error> {
    debug!("fork {:?}", fork);

    token.verify(&fork.token)?;

    let uid = crate::get_user_id(&jar)?;
    let mut it = p.repo.split(":");
    let repo = it.next().unwrap();
    let channel = it.next().unwrap_or("");

    let mut db = config.db.get().await?;
    let (repo, _) = crate::repository::repository_id(&mut db, &p.owner, &repo, uid, Perm::EDIT_CHANNELS)
        .await?;

    let redirect = format!("/{}/{}:{}", p.owner, p.repo, fork.name);
    debug!("fork id = {:?}", repo);
    config
        .replicator
        .handle_update(
            None,
            None,
            None,
            ::replication::Update::Fork {
                repo,
                channel: if channel.is_empty() {
                    MAIN_CHANNEL.to_string()
                } else {
                    channel.to_string()
                },
                new: fork.name,
            },
        )
        .await
        .unwrap();

    Ok(Redirect::to(&redirect).into_response())
}

async fn rename(
    State(config): State<crate::Config>,
    jar: SignedCookieJar,
    p: RepoPath,
    token: axum_csrf::CsrfToken,
    Form(rename): Form<Fork>,
) -> Result<Response, crate::Error> {
    debug!("rename {:?} {:?}", p, rename);
    if p.channel.as_deref().unwrap_or(MAIN_CHANNEL) == MAIN_CHANNEL {
        debug!("trying to rename main channel");
        return Ok(Redirect::to(&format!("/{}/{}", p.owner, p.repo)).into_response())
    }
    token.verify(&rename.token)?;

    let uid = crate::get_user_id(&jar)?;
    let mut db = config.db.get().await?;
    let (repo, _) =
        crate::repository::repository_id(&mut db, &p.owner, &p.repo, uid, Perm::EDIT_CHANNELS)
        .await?;
    let redirect = format!("/{}/{}:{}", p.owner, p.repo, rename.name);
    config
        .replicator
        .handle_update(
            None,
            None,
            None,
            ::replication::Update::Rename {
                repo,
                channel: p.channel.unwrap_or_else(|| MAIN_CHANNEL.to_string()),
                new: rename.name,
            },
        )
        .await?;

    Ok(Redirect::to(&redirect).into_response())
}

#[derive(Debug, Deserialize)]
pub struct Prune {
    token: String,
}

async fn prune(
    State(config): State<crate::Config>,
    jar: SignedCookieJar,
    p: RepoPath,
    token: axum_csrf::CsrfToken,
    Form(prune): Form<Prune>,
) -> Result<Response, crate::Error> {
    debug!("prune {:?}", prune);
    if p.channel.as_deref().unwrap_or(MAIN_CHANNEL) == MAIN_CHANNEL {
        return Ok(Redirect::to(&format!("/{}/{}", p.owner, p.repo)).into_response())
    }
    token.verify(&prune.token)?;
    let uid = crate::get_user_id(&jar)?;
    let mut db = config.db.get().await.unwrap();
    let (repo, _) =
        crate::repository::repository_id(&mut db, &p.owner, &p.repo, uid, Perm::EDIT_CHANNELS)
            .await?;
    config
        .replicator
        .handle_update(
            None,
            None,
            None,
            ::replication::Update::Prune {
                repo,
                channel: p.channel.unwrap_or_else(|| MAIN_CHANNEL.to_string()),
            },
        )
        .await?;
    Ok(Redirect::to(&format!("/{}/{}", p.owner, p.repo)).into_response())
}