package jp.agentec.abook.abv.ui.home.activity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

import org.json.adf.JSONArray;
import org.json.adf.JSONObject;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import jp.agentec.abook.abv.bl.acms.client.AcmsClient;
import jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.GetOperationDataParameters;
import jp.agentec.abook.abv.bl.acms.type.OperationType;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant;
import jp.agentec.abook.abv.bl.common.constant.ABookKeys;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.nw.NetworkAdapter;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.OperationContentDao;
import jp.agentec.abook.abv.bl.data.dao.OperationDao;
import jp.agentec.abook.abv.bl.data.dao.ReportStatusDao;
import jp.agentec.abook.abv.bl.data.dao.TaskReportDao;
import jp.agentec.abook.abv.bl.download.ContentFileExtractor;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.OperationContentDto;
import jp.agentec.abook.abv.bl.dto.OperationDto;
import jp.agentec.abook.abv.bl.dto.ReportStatusDto;
import jp.agentec.abook.abv.bl.dto.TaskDto;
import jp.agentec.abook.abv.bl.dto.TaskReportDto;
import jp.agentec.abook.abv.bl.dto.comparator.ReportStatusCompalator;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.LockReportLogic;
import jp.agentec.abook.abv.bl.logic.OperationLogic;
import jp.agentec.abook.abv.bl.logic.ReportStatusLogic;
import jp.agentec.abook.abv.bl.logic.UnlockReportLogic;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.constant.ErrorCode;
import jp.agentec.abook.abv.ui.home.helper.ActivityHandlingHelper;
import jp.agentec.abook.abv.ui.viewer.view.CheckFormWebview;
import jp.agentec.adf.util.DateTimeFormat;
import jp.agentec.adf.util.DateTimeUtil;

import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.OperationId;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportList;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportLockTime;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportLockUserId;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportLockUserName;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportStartDate;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.ReportStatusId;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.SendBackUserId;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.SendBackUserName;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskCode;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskKey;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskReportId;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationDataJSON.TaskReportInfo;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationListJSON.OperationList;
import static jp.agentec.abook.abv.bl.acms.client.json.OperationListJSON.OperationName;
import static jp.agentec.abook.abv.bl.common.Constant.HelpViewType.OperationDashboard;
import static jp.agentec.abook.abv.bl.common.Constant.ReportType.RoutineTask;
import static jp.agentec.abook.abv.bl.common.constant.ABookKeys.CMD_GET_REPORT_LIST;
import static jp.agentec.abook.abv.bl.common.constant.ABookKeys.CMD_GET_REPORT_STATUS_COUNT;
import static jp.agentec.abook.abv.bl.common.constant.ABookKeys.CMD_GO_REPORT_DETAIL;
import static jp.agentec.abook.abv.bl.common.constant.ABookKeys.CMD_LOCK_REPORT;
import static jp.agentec.abook.abv.bl.common.constant.ABookKeys.CMD_UNLOCK_REPORT;

public class DashboardActivity extends OperationActivity {
    private static final String TAG = "DashboardActivity";
    private static final String DASHBOARD_URL = "file:///android_asset/dashboard/app/index.html?app=android";

    private WebView webView;
    private ProgressBar progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ac_operation_dashboard);
        createCommonToolber();

        ImageButton reloadButton = findViewById(R.id.btn_reload);
        reloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Logger.d(TAG, "ReloadUrl");
                webView.loadUrl(DASHBOARD_URL);
            }
        });

        ImageButton helpButton = findViewById(R.id.btn_help);
        helpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showHelpViewDialog(OperationDashboard);
            }
        });

        progress = findViewById(R.id.refresh_prog);

        createWebView();
        FrameLayout frameLayout = findViewById(R.id.frameWebView);
        frameLayout.addView(webView, new LinearLayout.LayoutParams(FP, FP));

        // 読み込み
        webView.loadUrl(DASHBOARD_URL);
    }

    @Override
    protected void onResume() {
        super.onResume();
        try {
            loadAllOperationTask();
            ReportStatusLogic logic = new ReportStatusLogic();
            logic.loadReportStatus();
            getReportStatusCount();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (webView != null) {
            webView.stopLoading();
        }
    }

    @Override
    protected void createCommonToolber() {
        super.createCommonToolber();
        mDashboardButton.setEnabled(false);
    }

    private void loadAllOperationTask() {
        try {
            String path = ABVDataCache.getInstance().getUrlPath();
            NetworkAdapter networkAdapter = ABVEnvironment.getInstance().networkAdapter;
            OperationDao operationDao = AbstractDao.getDao(OperationDao.class);
            TaskReportDao taskReportDao = AbstractDao.getDao(TaskReportDao.class);
            String sid = ABVDataCache.getInstance().getMemberInfo().sid;

            for (OperationDto operation: operationDao.getAllOperation()) {
                GetOperationDataParameters param = new GetOperationDataParameters(sid, operation.operationId);
                OperationDataJSON json = AcmsClient.getInstance(path, networkAdapter).getOpereationData(param);
                for (TaskDto task : json.taskDtoList) {
                    for (TaskReportDto report : task.taskReportDtoList) {
                        taskReportDao.updateReportLock(
                                report.taskKey,
                                (long) report.taskReportId,
                                report.reportStartDate,
                                report.reportStatus,
                                report.reportLockUserId,
                                report.reportLockUserName,
                                report.reportLockTime
                        );
                    }
                }
            }
        } catch (Exception e) {
            Logger.e(TAG, e.getLocalizedMessage());
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void createWebView() {
        webView = new CheckFormWebview(this, new CheckFormWebview.KeyActionCallback() {

            @Override
            public void keyActionCallback(int editorInfoAction) {
                if (editorInfoAction == EditorInfo.IME_ACTION_GO || editorInfoAction == EditorInfo.IME_ACTION_NEXT) {
                    // キーボードの次ボタン検知
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Logger.i(TAG, "javascript:CHK.notifyMoveForcus()");
                            webView.loadUrl("javascript:CHK.notifyMoveForcus()");
                        }
                    });
                }
            }
        });
        webView.setVisibility(View.VISIBLE);
        if (Logger.isDebugEnabled()) {
            WebView.setWebContentsDebuggingEnabled(true); //デバッグモード（chromeからinspect可)
        }
        initSettings(webView.getSettings());
        webView.setWebChromeClient(new ChromeClient());
        webView.setWebViewClient(new ViewClient());

        webView.addJavascriptInterface(new JsInf(), "android");
        webView.getSettings().setJavaScriptEnabled(true); // JavaScriptを有効にする

        Logger.d(TAG, "loadUrl: " + DASHBOARD_URL);
    }

    private void initSettings(WebSettings settings) {

        settings.setSupportMultipleWindows(true); // 新しいウィンドウを開くイベントを取得する
        settings.setLoadsImagesAutomatically(true); // イメージを自動的にロードする
        settings.setBuiltInZoomControls(true); // ズーム機能を有効にする
        settings.setSupportZoom(true); // ズーム機能を有効にする
        //settings.setJavaScriptEnabled(true); // JavaScriptを有効にする
        settings.setLoadWithOverviewMode(true); // 画面の横幅にページの横幅を合わせる
        settings.setUseWideViewPort(true); // 画面の横幅にページの横幅を合わせる
        //noinspection deprecation(API18から非推奨になった。無視)
        settings.setPluginState(WebSettings.PluginState.ON); // 「EventHub.removeMessages(int what = 107) is not supported before the WebViewCore is set up.」のエラー対応（あまり効果ない？）
        settings.setDomStorageEnabled(true); // WebStorage有効化
        settings.setDisplayZoomControls(false);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        settings.setAllowFileAccessFromFileURLs(true);  //Android7利用で警告ダイヤログ表示問題対応
        //ターゲットバージョン30以上からデフォルトがfalseに設定されている問題対応
        settings.setAllowFileAccess(true);
    }

    private class ChromeClient extends WebChromeClient {

        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (progress != null) {
                progress.setVisibility(View.VISIBLE);
                if (newProgress >= 100) {
                    progress.setVisibility(View.GONE);
                }
            }
        }
    }

    private class JsInf {

        @JavascriptInterface
        public void existSetLocation(String ret) {
            Logger.d(TAG, "existSetLocation=%s", ret);
            if (ret != null && ret.equals("true")) { // setLocationメソッドが存在する場合、ページ読み込み完了とみなす
                //isPageFinished = true;
            } else { // 存在しない場合、ページ読み込み未完了とみなし１秒後に再度呼出しを繰り返す
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //callExistsSetLocation();
                    }
                }, 1000);
            }
        }

        @JavascriptInterface
        public void existSendLog(String ret) {
            Logger.d(TAG, "existSendLog=%s", ret);
            if (ret != null && ret.equals("true")) {
                runOnUiThread(new Runnable() { // sendLogメソッドが存在する場合、呼び出す
                    @Override
                    public void run() {
                        try {
                            webView.loadUrl("javascript:sendLog()");
                        } catch (Exception e) {
                            Logger.e(TAG, "javascript:sendLog error. " + e.toString());
                        }
                    }
                });
            } else {
                finish();
            }
        }

        @JavascriptInterface
        public String getCachePath() {
            return getCacheDir().getAbsolutePath();
        }

        @JavascriptInterface
        public void sendParam(String param) {
            Logger.i(TAG, "sendParam: %s", param);

            final JSONObject json = new JSONObject(param);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    checkApiLoading(json);
                }
            });
        }

        @JavascriptInterface
        public void getAttachedDataUrl(String taskKey, String data) {}
    }

    private static class ViewClient extends WebViewClient {

        @SuppressWarnings("deprecation")
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Logger.e(TAG, "onReceivedError errorCode=%s, description=%s, failingUrl=%s", errorCode, description, failingUrl);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Logger.v(TAG, "shouldOverrideUrlLoading: %s", url);
            return false;
        }
    }

    private void checkApiLoading(JSONObject json) {

        String cmd = json.getString(ABookKeys.CMD);
        if (cmd == null) {
            return;
        }

        try {
            switch (cmd) {
                case CMD_GET_REPORT_STATUS_COUNT: {
                    getReportStatusCount();
                    break;
                }
                case CMD_GET_REPORT_LIST: {
                    int reportStatusId = json.getInt(ReportStatusId);
                    getReportList(reportStatusId);
                    break;
                }
                case CMD_GO_REPORT_DETAIL: {
                    long operationId = json.getLong(OperationId);
                    String taskKey = json.has(TaskKey) ? json.getString(TaskKey) : null;
                    Long taskReportId = json.has(TaskReportId) ? json.getLong(TaskReportId) : null;
                    String reportStartDate = json.has(ReportStartDate) ? json.getString(ReportStartDate) : null;
                    goReportDetail(operationId, taskKey, taskReportId, reportStartDate);
                    break;
                }
                case CMD_LOCK_REPORT: {
                    String taskKey = json.getString(TaskKey);
                    long taskReportId = json.has(TaskReportId) ? json.getLong(TaskReportId) : 0;
                    Date reportStartDate = json.has(ReportStartDate) ? getDateOrNull(json.getString(ReportStartDate)) : null;
                    lockReport(taskKey, taskReportId, reportStartDate);
                    break;
                }
                case CMD_UNLOCK_REPORT: {
                    String taskKey = json.has(TaskKey) ? json.getString(TaskKey) : "";
                    long taskReportId = json.has(TaskReportId) ? json.getLong(TaskReportId) : 0;
                    Date reportStartDate = json.has(ReportStartDate) ? getDateOrNull(json.getString(ReportStartDate)) : null;
                    unlockReport(taskKey, taskReportId, reportStartDate);
                    break;
                }
            }
        } catch (Throwable e) {
            Logger.e(TAG, e);
        }
    }

    private void getReportStatusCount() throws IOException {
        ReportStatusDao dao = AbstractDao.getDao(ReportStatusDao.class);
        JSONObject extParam = dao.getReportStatusCountJson();
        saveCounts(extParam);
    }

    private void getReportList(int reportStatusId) {
        ReportStatusDao dao = AbstractDao.getDao(ReportStatusDao.class);
        List<ReportStatusDto> reports = null;
        switch (reportStatusId) {
            case 0: {
                reports = dao.getNotStartedReport();
                break;
            }
            case 1: {
                reports = dao.getWorkingReport();
                break;
            }
            case 2: {
                reports = dao.getCompleteOkReport();
                break;
            }
            case 3: {
                reports = dao.getCompleteNgReport();
                break;
            }
            case 4: {
                reports = dao.getIncompleteReport();
                break;
            }
            case 5: {
                reports = dao.getAlertReport();
                break;
            }
            case 6: {
                reports = dao.getSendBackedReport();
                break;
            }
            case 7: {
                reports = dao.getPendingReport();
            }
        }
        if (reports != null) {
            JSONObject param = makeReportTree(reportStatusId, reports);
            String script = "javascript:CHK_Dashboard.reportListCallback(" + reportStatusId + "," + param.toString() + ")";
            webView.loadUrl(script);
        }
    }

    private JSONObject makeReportTree(int reportStatusId, List<ReportStatusDto> reports) {
        JSONObject tree = new JSONObject();

        tree.put(ReportStatusId, reportStatusId);

        JSONArray operationList = new JSONArray();
        tree.put(OperationList, operationList);

        // operationIDで、ソートしておく
        Collections.sort(reports, new ReportStatusCompalator());

        JSONObject operation = null;
        for (ReportStatusDto report : reports) {
            if (operation == null || operation.getLong(OperationId) != report.getOperationId()) {
                // 新しい作業の追加
                operation = new JSONObject();
                operation.put(OperationId, report.getOperationId());
                operation.put(OperationName, report.getOperationName());
                operation.put(ReportList, new JSONArray());
                tree.getJSONArray("operationList").put(operation);
            }
            JSONObject task = new JSONObject();
            task.put(TaskKey, report.getTaskKey());
            task.put(TaskReportInfo, report.getTaskReportInfo());
            task.put(TaskReportId, report.getTaskReportId());
            task.put(TaskCode, report.getTaskCode());
            task.put(ReportStartDate, report.getReportStartDateAsString());
            task.put(ReportLockUserId, report.getReportLockUserId());
            task.put(ReportLockUserName, report.getReportLockUserName());
            task.put(ReportLockTime, report.getReportLockTimeAsString());
            task.put(SendBackUserId, report.getSendBackUserId());
            task.put(SendBackUserName, report.getSendBackUserName());
            operation.getJSONArray(ReportList).put(task);
        }
        return tree;
    }

    private void goReportDetail(long operationId, String taskKey, Long taskReportId, String reportStartDate) {
        if (ActivityHandlingHelper.getInstance().isMeetingConnected()) {
            return;
        }
        // 新着更新を止める
        contentRefresher.stopRefresh();

        OperationDao operationDao = AbstractDao.getDao(OperationDao.class);
        OperationDto operationDto = operationDao.getOperation(operationId);
        // t_operationテーブルにcontent_idは含まれていないので、この時点でoperationDto.contentIdはnull

        // なので、contentDtoを取り出す
        OperationContentDao operationContentDao = AbstractDao.getDao(OperationContentDao.class);
        OperationContentDto operationContentDto = operationContentDao.getOperationMainContent(operationId);

        ContentDto contentDto = contentDao.getContent(operationContentDto.contentId);

        try {
            if (contentDto != null && contentDto.downloadedFlg) {
                String contentPath = ABVEnvironment.getInstance().getTaskListDirName(ContentFileExtractor.getInstance().getContentCacheDirWithExtract(contentDto.contentId));

                // プロジェクトの指示/報告表示時、必要なJSONファイル作成
                OperationLogic operationLogic = AbstractLogic.getLogic(OperationLogic.class);
                operationLogic.createJsonForOperationContent(operationDto.operationId, contentPath, operationDto.reportType == RoutineTask);

                // サーバ作業後、対応必要
                StringBuilder path = new StringBuilder();
                path.append(contentPath);
                // #32926 作業報告画面改善 start
                path.append("/index.html?app=android");
                path.append("&report_type=").append(operationDto.reportType);  // 作業報告タイプ : 0:報告 1:定期点検 2:報告(回答)
                path.append("&mobile_flg=").append(isNormalSize() ? "1" : "0");  // ScreenType
                if (operationDto.reportType == RoutineTask) {
                    path.append("&taskKey=").append(taskKey);
                    path.append("&taskReportId=").append(taskReportId);
                    path.append("&reportStartDate=").append(reportStartDate);
                } else {
                    path.append("&taskKey=").append(taskKey);
                }

                Logger.d(TAG, "path : " + path);
                Intent intent = new Intent();
                intent.putExtra(ABookKeys.CONTENT_ID, operationDto.contentId);
                intent.putExtra(ABookKeys.OPERATION_ID, operationDto.operationId);
                intent.putExtra(Constant.ABookCheck.XWALK_OPEN_TYPE, Constant.XWalkOpenType.TASK_REPORT);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

                if(operationDto.operationType == OperationType.PDF) {
                    intent.putExtra("LINKURL", "file://" + path);
                    ActivityHandlingHelper.getInstance().checkContentActivity(operationDto.contentId, 0, intent);
                } else {
                    ActivityHandlingHelper.getInstance().startHTMLWebActivity(this, intent, "file://" + path, contentDto.contentId, -1, -1, -1, -1, -1);
                }
            } else {
                Logger.w(TAG, "content is not download");
            }
        } catch (Exception e) {
            Logger.e(TAG, e);
            handleErrorMessageToast(ErrorCode.E107);
        }
    }

    private void lockReport(String taskKey, Long taskReportId, Date reportStartDate) {
        try {
            LockReportLogic.Result r = LockReportLogic.newInstance().lock(taskKey, taskReportId, reportStartDate);
            // JSコールバック
            afterABookCheckApi(
                    CMD_LOCK_REPORT,
                    taskKey,
                    r.getResult(),
                    r.getMessage(),
                    r.getExtParam().json()
            );
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void unlockReport(String taskKey, Long taskReportId, Date reportStartDate) {
        try {
            UnlockReportLogic.Result r = UnlockReportLogic.newInstance().unlock(taskKey, taskReportId, reportStartDate);
            // JSコールバック
            afterABookCheckApi(
                    CMD_UNLOCK_REPORT,
                    taskKey,
                    r.getResult(),
                    r.getMessage(),
                    r.getExtParam().json()
            );
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public void afterABookCheckApi(final String cmd, final String taskKey, final int result, final String message, final String extParam) {
        Logger.v(TAG, "run javaScript for ABookCheck : cmd=%s, taskKey=%s, result=%s, message=%s", cmd, taskKey, result, message);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String callback = String.format("javascript:CHK.afterABookCheckApi('%s', '%s', '%s', '%s', %s)", cmd, taskKey, result, message, extParam);
                Logger.i(TAG, callback);
                webView.loadUrl(callback);
            }
        });
    }

    private Date getDateOrNull(String s) {
        try {
            return DateTimeUtil.toDate(s, "UTC", DateTimeFormat.yyyyMMddHHmmss_hyphen);
        } catch (Exception e) {
            Logger.e(TAG, e.getLocalizedMessage());
            return null;
        }
    }

    public String getCachePath() {
        return getCacheDir().getAbsolutePath();
    }

    private void saveCounts(JSONObject counts) throws IOException {
        File dir = new File(getCachePath());
        File file = new File(dir, "getReportStatusCount.json");

        try(FileWriter writer = new FileWriter(file)) {
            writer.write(counts.toString());
        } catch (Throwable e) {
            Logger.e(TAG, e);
            throw e;
        }
        // 強制的に画面更新するためにリロード fixme
        webView.loadUrl(DASHBOARD_URL);
    }
}
