package com.lemenzo.contacts;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.*;
import android.os.Handler;
import android.os.Looper;

public class ImportExportActivity extends Activity {
    private static final int OPEN_VCF = 601;
    private static final int OPEN_CSV = 602;
    private static final int SAVE_VCF = 603;
    private static final int SAVE_CSV = 604;
    private static final int REQ_CONTACTS = 715;

    private static final String[] VCF_MIME_TYPES = {
            "text/vcard",
            "text/x-vcard",
            "text/directory",
            "application/vcard",
            "application/x-vcard",
            "application/octet-stream"
    };
    private static final String[] CSV_MIME_TYPES = {
            "text/csv",
            "text/comma-separated-values",
            "application/csv",
            "application/vnd.ms-excel",
            "text/plain",
            "application/octet-stream"
    };

    private ContactsRepository repo;
    private final ExecutorService io = Executors.newSingleThreadExecutor(r -> new Thread(r, "LemenzoContacts-ImportExport"));
    private final Handler main = new Handler(Looper.getMainLooper());
    private volatile boolean destroyed;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        repo = new ContactsRepository(this);
        if (!ContactsPermission.hasReadWrite(this)) {
            ContactsPermission.requestReadWrite(this, REQ_CONTACTS);
            return;
        }
        build();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode != REQ_CONTACTS) return;
        if (ContactsPermission.hasReadWrite(this)) {
            build();
        } else {
            Toast.makeText(this, R.string.contacts_permission_required, Toast.LENGTH_LONG).show();
            finish();
        }
    }

    private void build() {
        LinearLayout root = new LinearLayout(this);
        root.setOrientation(LinearLayout.VERTICAL);
        root.setBackgroundColor(Ui.BG);
        Ui.applySafeInsets(this, root);
        root.addView(Ui.subHeader(this, getString(R.string.import_export)));

        ScrollView scroll = new ScrollView(this);
        LinearLayout box = new LinearLayout(this);
        box.setOrientation(LinearLayout.VERTICAL);
        box.setPadding(Ui.dp(this, 16), 0, Ui.dp(this, 16), Ui.dp(this, 24));

        section(box, R.string.import_export);
        action(box, R.string.import_vcf, ".vcf", () -> open(OPEN_VCF));
        action(box, R.string.import_csv, ".csv", () -> open(OPEN_CSV));
        section(box, R.string.export_vcf);
        action(box, R.string.export_vcf, ".vcf", () -> save("text/vcard", "contacts.vcf", SAVE_VCF));
        action(box, R.string.export_csv, ".csv", () -> save("text/csv", "contacts.csv", SAVE_CSV));
        section(box, R.string.other);
        action(box, R.string.duplicates, getString(R.string.potential_duplicates),
                () -> startActivity(new Intent(this, DuplicateActivity.class)));

        scroll.addView(box);
        root.addView(scroll, new LinearLayout.LayoutParams(-1, 0, 1));
        setContentView(root);
    }

    private void section(LinearLayout box, int stringId) {
        box.addView(Ui.space(this, 12));
        box.addView(Ui.section(this, getString(stringId)));
        box.addView(Ui.space(this, 7));
    }

    private void action(LinearLayout box, int title, String subtitle, Runnable action) {
        LinearLayout row = Ui.raisedCard(this);
        row.setOrientation(LinearLayout.HORIZONTAL);
        row.setGravity(Gravity.CENTER_VERTICAL);
        ImageView icon = new ImageView(this);
        icon.setImageResource(R.drawable.ic_more);
        icon.setColorFilter(Ui.BLUE);
        row.addView(icon, new LinearLayout.LayoutParams(Ui.dp(this, 28), Ui.dp(this, 28)));
        LinearLayout text = new LinearLayout(this);
        text.setOrientation(LinearLayout.VERTICAL);
        text.setPadding(Ui.dp(this, 12), 0, 0, 0);
        text.addView(Ui.title(this, getString(title), 14));
        text.addView(Ui.text(this, subtitle, 12, Ui.SECONDARY));
        row.addView(text, new LinearLayout.LayoutParams(0, -2, 1));
        TextView arrow = Ui.text(this, "›", 24, Ui.SECONDARY);
        row.addView(arrow);
        row.setOnClickListener(v -> action.run());
        box.addView(row);
        box.addView(Ui.space(this, 8));
    }

    private void open(int requestCode) {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT)
                .addCategory(Intent.CATEGORY_OPENABLE)
                .setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES,
                requestCode == OPEN_VCF ? VCF_MIME_TYPES : CSV_MIME_TYPES);
        startActivityForResult(intent, requestCode);
    }

    private void save(String type, String name, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
                .addCategory(Intent.CATEGORY_OPENABLE)
                .setType(type)
                .putExtra(Intent.EXTRA_TITLE, name);
        startActivityForResult(intent, requestCode);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK || data == null || data.getData() == null) return;
        Uri uri = data.getData();

        if (requestCode == OPEN_CSV) {
            if (!isExpectedFile(uri, false)) {
                Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
                return;
            }
            startActivity(new Intent(this, CsvImportActivity.class)
                    .setData(uri).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
            return;
        }

        if (requestCode == OPEN_VCF && !isExpectedFile(uri, true)) {
            Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
            return;
        }

        io.execute(() -> {
            try {
                if (requestCode == OPEN_VCF) {
                    final int[] imported={0}, failed={0};
                    try (InputStream in=getContentResolver().openInputStream(uri)) {
                        if(in==null)throw new IllegalStateException("Unable to open VCF");
                        int parsed=ContactFileCodec.readVcfBatched(in,80,batch->{
                            int ok=repo.importContacts(batch);
                            imported[0]+=ok;
                            failed[0]+=batch.size()-ok;
                        });
                        if(parsed==0)throw new IllegalStateException("No contacts");
                    }
                    postToast(getString(R.string.import_result,imported[0],0,failed[0]),true);
                } else if (requestCode == SAVE_VCF) {
                    try(OutputStream out=getContentResolver().openOutputStream(uri,"wt")){
                        if(out==null)throw new IllegalStateException("Unable to open VCF output");
                        repo.exportContactsBatched(200,batch->ContactFileCodec.writeVcf(out,batch));
                    }
                    postToast(getString(R.string.export_complete),false);
                } else if (requestCode == SAVE_CSV) {
                    try(OutputStream out=getContentResolver().openOutputStream(uri,"wt")){
                        if(out==null)throw new IllegalStateException("Unable to open CSV output");
                        final boolean[] header={true};
                        repo.exportContactsBatched(200,batch->{ContactFileCodec.writeCsv(out,batch,header[0]);header[0]=false;});
                    }
                    postToast(getString(R.string.export_complete),false);
                }
            } catch(Exception e) {
                postToast(getString(requestCode==OPEN_VCF?R.string.import_failed:R.string.export_failed),true);
            }
        });
    }

    private void postToast(String text, boolean longToast){
        main.post(()->{
            if(!destroyed)Toast.makeText(this,text,longToast?Toast.LENGTH_LONG:Toast.LENGTH_SHORT).show();
        });
    }

    private boolean isExpectedFile(Uri uri, boolean vcf) {
        String name = displayName(uri).toLowerCase(Locale.ROOT);
        if (vcf && name.endsWith(".vcf")) return true;
        if (!vcf && name.endsWith(".csv")) return true;
        String mime = getContentResolver().getType(uri);
        if (mime == null) return false;
        mime = mime.toLowerCase(Locale.ROOT);
        if (vcf) {
            return mime.equals("text/vcard") || mime.equals("text/x-vcard")
                    || mime.equals("text/directory") || mime.equals("application/vcard")
                    || mime.equals("application/x-vcard");
        }
        return mime.equals("text/csv") || mime.equals("text/comma-separated-values")
                || mime.equals("application/csv") || mime.equals("application/vnd.ms-excel")
                || mime.equals("text/plain");
    }

    private String displayName(Uri uri) {
        try (Cursor cursor = getContentResolver().query(uri,
                new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
            if (cursor != null && cursor.moveToFirst() && !cursor.isNull(0)) return cursor.getString(0);
        } catch (RuntimeException ignored) {}
        return "";
    }


    @Override protected void onDestroy(){
        destroyed=true;
        io.shutdownNow();
        super.onDestroy();
    }

}