package fr.android.pointspermis.web;

import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import fr.android.pointspermis.MainActivity;
import fr.android.pointspermis.captcha.Captcha;
import fr.android.pointspermis.web.HTTPSConnect;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.util.*;

/**
 * Created by vins on 07/01/15.
 */
public class WebTask extends AsyncTask<String, String, String> {

    private final int RETRY = 3;
    private final int RETRY_CAPTCHA = 15;

    private final String IDENTIFICATION_URL = "https://tele7.interieur.gouv.fr/telepoints/identification.do";
    private final String CONSULTATION_URL = "https://tele7.interieur.gouv.fr/telepoints/consultation.do";
    private final String CAPTCHA_URL = "https://tele7.interieur.gouv.fr/telepoints/jcaptcha?id=n&rnd=";
    private final String TOKEN_NAME = "org.apache.struts.taglib.html.TOKEN";
    private final String CERTIFICATE = "tele7.interieur.gouv.fr.cert";

    private final Context context;
    private MainActivity main;
    private ProgressDialog pDialog;
    private List<String> result;

    public WebTask(Context context, MainActivity main) {
        this.context = context;
        this.main = main;

        result = new ArrayList<>();

        System.setProperty("https.protocols", "TLSv1");
    }

    @Override
    protected String doInBackground(String... inputs) {

        int count = 0, captcha = 0;
        List<String> infos = null;

        while (count < RETRY && captcha < RETRY_CAPTCHA) {
            infos = getInfos(inputs[0], inputs[1], inputs[2], inputs[3]);
            if (infos == null) {
                // Return null only if a big problem happened
                return null;
            }
            if (infos.isEmpty()) {
                // Same as previous
                return null;
            }
            if (infos.size() == 1) {
                if (infos.get(infos.size()-1).equals("RETRY")) {
                    captcha++; count = 0;
                    continue;
                }
                if (infos.get(infos.size()-1).contains("ERROR")) {
                    count++; captcha = 0;
                    continue;
                }
                // Also a big problem (should never happen)
                return null;
            }
            if (infos.size() == 3) {
                result.addAll(infos);
                return "OK";
            }
        }

        // Return the original error
        return infos.get(0);
    }

    public List<String> getInfos(String code1, String code2, String code3, String pwd) {

        List<String> infos = new ArrayList<>();
        result.clear();

        // Create and init a new connection
        HTTPSConnect connection;
        try {
            connection = new HTTPSConnect(main.getAssets().open(CERTIFICATE));
        } catch (Exception e) {
            Log.e("ERROR", "Cannot Init HTTPSConnect: " + e.getMessage());
            return Collections.singletonList("ERROR INIT");
        }

        // Get the identification html page
        Document docId = null;
        try {
            docId = connection.getHTML(IDENTIFICATION_URL);
        } catch (Exception e) {
            Log.e("ERROR", "Document initialisation error: " + e.getMessage());
            return Collections.singletonList("ERROR GET");
        }

        // Parse HTML page and retrieve all needed infos
        String token = docId.body().getElementsByAttributeValue("name", TOKEN_NAME).attr("value");
        String dossier1_name = docId.body().getElementById("dossier1").attr("name");
        String dossier2_name = docId.body().getElementById("dossier2").attr("name");
        String dossier3_name = docId.body().getElementById("dossier3").attr("name");
        String code_name = docId.body().getElementsByAttributeValue("type", "password").attr("name");
        String captcha_name = docId.body().getElementsByAttributeValueContaining("name", "captcha_response").attr("name");

        // Generate a random click position over the send button
        int xMin = 5, xMax = 138;
        int yMin = 5, yMax = 21;
        int x = new Random().nextInt((xMax - xMin) + 1) + xMin;
        int y = new Random().nextInt((yMax - yMin) + 1) + yMin;

        // Get the captcha img
        Bitmap img = null;
        try {
            img = connection.getImage(CAPTCHA_URL + code_name.replace("value(", "").replace("code)", ""));
        } catch (Exception e) {
            Log.e("ERROR", "Image downloading error: " + e.getMessage());
            return Collections.singletonList("ERROR CAPTCHA");
        }

        // Decoding captcha
        Captcha captcha = new Captcha(context.getFilesDir().getPath());
        String code_captcha = captcha.getCode(img);
        if (code_captcha == null) {
            Log.e("ERROR", "Error while decoding captcha");
            return Collections.singletonList("ERROR OCR");
        }

        // Set request parameters
        Map<String, String> urlParams = new HashMap<>();
        urlParams.put("y", Integer.toString(y));
        urlParams.put("x", Integer.toString(x));
        urlParams.put(captcha_name, code_captcha);
        urlParams.put(code_name, pwd);
        urlParams.put(dossier3_name, code3);
        urlParams.put(dossier2_name, code2);
        urlParams.put(dossier1_name, code1);
        urlParams.put(TOKEN_NAME, token);

        // Send the post request
        Document docCons = null;
        try {
            docCons = connection.postData(CONSULTATION_URL, urlParams);
        } catch (Exception e) {
            Log.e("ERROR", "POST request error: " + e.getMessage());
            return Collections.singletonList("ERROR POST");
        }

        // Clean connection cookies
        connection.clear();

        // Get the solde
        Elements elts = docCons.getElementsContainingText("SUR UN CAPITAL DE");
        if (elts != null && !elts.isEmpty()) {
            String solde = elts.get(elts.size()-1).html();
            solde = solde.substring(0, solde.indexOf(' ')) + " / " +
                    solde.substring(solde.lastIndexOf(" ")+1, solde.length()-1);
            Log.i("INFO", "Solde: " + solde);
            infos.add(solde);

            // Get the name
            elts = docCons.getElementsContainingText("Bonjour");
            if (elts != null && !elts.isEmpty()) {
                String name = elts.get(elts.size()-1).html();
                name = name.replace("Bonjour, ", "");
                Log.i("INFO", "Name: " + name);
                infos.add(name);

                // Get the birthday
                elts = docCons.getElementsContainingText("Date de Naissance");
                if (elts != null && !elts.isEmpty()) {
                    String birthday = elts.get(elts.size()-1).html();
                    birthday = birthday.replace("Date de Naissance", "").replace("&nbsp;", "").replace(" ", "");
                    Log.i("INFO", "Birthday: " + birthday);
                    infos.add(birthday);
                }
            }
        }
        if (infos.size() < 3) {
            elts = docCons.getElementsByClass("rouge12b");
            if (!elts.isEmpty()) {
                // CAPTCHA ERROR
                if (elts.get(elts.size()-1).html().contains("Le code de s")) {
                    Log.e("ERROR", "Wrong captcha !");
                    return Collections.singletonList("RETRY");
                } else { // USER ERROR
                    if (elts.get(elts.size()-1).html().contains("Ce dossier n'existe pas")) {
                        Log.e("ERROR", "Dossier num is unknown !");
                        return Collections.singletonList("ERROR DOSSIER");
                    }
                    if (elts.get(elts.size()-1).html().contains("ro de dossier est incorrect")) {
                        Log.e("ERROR", "Dossier num is invalid !");
                        return Collections.singletonList("ERROR DOSSIER");
                    }
                    if (elts.get(elts.size()-1).html().contains("Le code confidentiel est incorrect")) {
                        Log.e("ERROR", "Personal code is wrong !");
                        return Collections.singletonList("ERROR CODE");
                    }
                    Log.e("ERROR", "Unknown error when parsing the result page !");
                    return Collections.singletonList("ERROR UNKNOWN");
                }
            }
            else {
                Log.e("ERROR", "Unable to parse the result webpage as expected, something has changed !");
                return Collections.singletonList("ERROR PAGE");
            }
        }
        else {
            return infos;
        }
    }

    @Override
    protected void onPreExecute() {
        pDialog = ProgressDialog.show(context, null, "Connection..", true);
    }

    @Override
    protected void onPostExecute(String s) {
        if(pDialog != null ){
            if(pDialog.isShowing()) {
                pDialog.dismiss();
                pDialog = null;
            }
        }

        if(s != null) {
            if (s.equals("OK")) {
                main.setInfos(result);
            }
            if (s.equals("RETRY")) {
                Toast.makeText(context, "Erreur: Une mise à jour de sécurité empêche la récupération des informations" +
                        ", veuillez vérifier que l'application est à jour.", Toast.LENGTH_LONG).show();
            }
            if (s.contains("ERROR")) {
                if (s.contains("DOSSIER")) {
                    Toast.makeText(context, "Erreur: Votre numéro de dossier n'existe pas.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("CODE")) {
                    Toast.makeText(context, "Erreur: Votre code personnel est incorrect.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("INIT")) {
                    Toast.makeText(context, "Erreur: Un problème est survenu lors de la préparation de la connexion " +
                                    "sécurisée au serveur.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("GET")) {
                    Toast.makeText(context, "Erreur: Impossible d'établir de connection, veuillez vérifier votre " +
                            "connectivité Internet.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("CAPTCHA")) {
                    Toast.makeText(context, "Erreur: Impossible de récupérer certains éléments, veuillez mettre à " +
                            "jour l'application.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("POST")) {
                    Toast.makeText(context, "Erreur: Un problème est survenu lors de l'envoi de la requête au " +
                            "serveur, veuillez mettre à jour l'application.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("OCR")) {
                    Toast.makeText(context, "Erreur: Un problème interne est survenu. Essayez de réinstaller " +
                            "l'application.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("PAGE")) {
                    Toast.makeText(context, "Erreur: Impossible de récupérer les données, veuillez mettre à jour " +
                            "l'application.", Toast.LENGTH_LONG).show();
                }
                if (s.contains("UNKNOWN")) {
                    Toast.makeText(context, "Erreur: Une erreur inconnue est survenue, veuillez mettre à jour " +
                            "l'application.", Toast.LENGTH_LONG).show();
                }
            }
            return;
        }

        Log.e("ERROR", "Unable to get result (onPostExecute) from WebForm");
        Toast.makeText(context, "Erreur: Une erreur fatale est survenue durant la récupération du solde, veuillez " +
                        "vérifier que l'application est à jour ou essayez de la réinstaller.", Toast.LENGTH_LONG).show();
    }
}
