/**
 * Schema required for implementing process
 */

import React from "react";

import Base from './../base';

import { Link } from 'react-router-dom'

import { Tag } from 'antd'

import ProcessDetail from './components/process-detail/process-detail';

import { Steps, ProcessTransactions, StepTransactions, Roles, Checklists } from './../';

import DateUtils from '../../utils/date/date.utils';

import ProcessDashboard from './components/process-dashboard/process-dashboard';


class Process extends Base {

    constructor() {

        super()

        this.fields = [{
            field: 'name',
            caption: 'Name'
        }, {
            field: 'description',
            caption: 'Description'
        }, {
            field: 'identifier',
            caption: 'Identifier'
        }, {
            field: 'active',
            type: 'radio',
            options: [true, false],
            caption: 'Active'
        }, {
            field: 'order',
            type: 'number',
            caption: 'Order'
        }
        ]

        // this.columns = [];
    }

    get getEndpoint() {
        return 'process'
    }

    get path() {
        return `process`
    }

    get getName() {
        return `process`
    }


    /**
     * Overriding get to stress for orderBy
     */
    async get(queries) {

        return this.getMethod(queries, { orderBy: 'order' })

    }

    /**
     * Start the process with a batch
     */
    startProcessBatch(batch, identifier, body) {

        // Starting a process is creating a process transaction record
        // and starting the first step of that process 
        // This involves first completing any sub step
        // it might have (any level of) and 

        var queries = [{
            field: 'identifier',
            value: identifier
        }]

        return this.get(queries).then(async (result) => {

            var process = result.process[0];

            // Trigger the process and its first sub process
            return await this.triggerProcessBatch(batch, process.id, body);

        })
    }

    /**
     * Move the process transaction to the next step 
     * 
     * @param {*} process_transaction_id 
     */
    async proceedNextStep({ id, process_transaction_id }) {

        // Get the firestore app
        const app = this.getFireStoreApp();

        // Starting a batched write
        let batch = app.batch();

        const transactions = await ProcessTransactions.getRecord(process_transaction_id)

        // console.log(transactions);

        const step = await Steps.getRecord(transactions.step_id);

        // const steps = await this.prepareStepData(id, step, transactions)

        // console.log(step, steps);

        return await this.onStepCompletion({ batch, step, process_transaction_id, record_id: id });

    }

    /**
     * Actions to be taken care on completion of a step
     */
    async onStepCompletion({ batch, step, process_transaction_id, record_id, callback }) {

        if (step.trigger_sub_process_ids === null) {
            step.trigger_sub_process_ids = []
        }

        if (step.trigger_process_ids === null) {
            step.trigger_process_ids = []
        }

        // The first Step transaction
        let step_transaction = step.step_transactions[0];

        // We have to forward the process transaction to the next sub process 
        // 
        // await triggerSubProcess(step, process_transaction_id)

        console.log("Triggering Subprocesses", step.trigger_sub_process_ids.map((record) => record.id));

        // Trigger the sub process
        await Promise.all(step.trigger_sub_process_ids.map(async (step_id) => {

            let params = {
                step_id: step_id
            }

            console.log("Updating Process Transaction started");

            // Reference for process transaction
            var processTransactionReference = ProcessTransactions.getRecordReference(process_transaction_id)

            // Move the process transaction to next step
            await batch.update(processTransactionReference, this.appendDefaultValues(params));

            console.log("Updating Process Transaction ended");

            // If there is a step transaction record , 
            // We have to update that its completed

            // We have to record a step transaction record for every event
            let body = {
                step_id: step_id,
                process_id: step.process_id,
                process_transaction_id: process_transaction_id,
                record_id: record_id
            }

            // Record the Step Transaction
            var stepTransactionReference = StepTransactions.getRecordReference()

            console.log("Step Transaction Add started");

            return await batch.set(stepTransactionReference, this.appendDefaultValues(body));

            console.log("Step Transaction Add Completed");

        }));

        console.log("Triggering Process started", step.trigger_process_ids && step.trigger_process_ids.map((record) => record.id));

        // We also have to trigger any process that this step would trigger
        await Promise.all(step.trigger_process_ids && step.trigger_process_ids.map((process_id) => {

            let body = {
                record_id: record_id,
                model_identifier: 'Candidates'
            }

            console.log("Trigger Process Started");

            return this.triggerProcessBatch(batch, process_id, body, process_transaction_id).catch((error) => {

                console.log(error, "Trigger process failed");
            })

        }));

        console.log("Step Transaction Update Started");

        if (step_transaction) {

            console.log("Found Step Transaction to Update");

            // We have to record a step transaction record for every event
            let body = {
                end_time: DateUtils.getTime(),
                completed: true,
            }

            // Record the Step Transaction
            let stepTransactionReference = StepTransactions.getRecordReference(step_transaction.id);

            await batch.update(stepTransactionReference, this.appendDefaultValues(body));

            // Commit the batch
            return batch.commit();

        } else {

            // Commit the batch
            return batch.commit();

        }
    }


    /**
     * 
     * Start the step
     * After identifying the step , we have
     * to check if this step has any sub steps 
     * If present we have to start the first step of that step
     * 
     * @param {*} param0 
     * @returns 
     */
    async startStepBatch({ batch, step, body }) {

        // Find all the steps in the process
        // After finding the steps we have to find all
        // the sub steps of the step , use a recursive apporach to make
        // sure 
        let stepParams = {
            ...body,

            step_id: step.id,
            process_id: step.process_id,

            start_time: new Date(),
            end_time: null,
            completed: false
        }

        const stepTransactionReference = StepTransactions.getRecordReference();

        console.log(stepTransactionReference.id);

        // Find the sub steps and start the loop  
        var queries = [{
            field: 'header_id',
            value: step.id
        }, {
            field: 'active',
            value: true
        }]

        // Find the first step of child
        const { steps } = await Steps.get(queries, {
            orderBy: 'order'
        });

        if (steps.length) {

            await batch.set(stepTransactionReference, StepTransactions.appendDefaultValues(stepParams))

            // Start the step
            await this.startStepBatch({ batch, step: steps[0], body });

        } else {

            stepParams = {
                ...stepParams,
                end_time: new Date(),
                completed: true
            }

            return await batch.set(stepTransactionReference, StepTransactions.appendDefaultValues(stepParams))

        }
    }

    /**
     * Stop the step
     */
    async stopStepBatch({ batch, step }) {

        // Complete the step
        // If another step needs to be started , start that step 
        // Else thats it 
        console.log(step);

        if (step.step_transactions.length) {

            // Get the reference
            let stepTransactionReference = StepTransactions.getRecordReference(step.step_transactions[0].id)

            await batch.update(stepTransactionReference, {
                end_time: new Date(),
                completed: true
            })

            // Further triggering of other steps
            return await batch.commit()
        }
    }

    /**
     * To Start the process
     * 
     * @param {*} batch 
     * @param {*} process_id 
     * @param {*} body 
     * @param {*} process_transaction_id 
     * @returns 
     */
    async triggerProcessBatch(batch, process_id, body, process_transaction_id) {

        var queries = [{
            field: 'process_id',
            value: process_id
        }, {
            field: 'active',
            value: true
        }]
        // Find all the steps in the process
        // After finding the steps we have to find all
        // the sub steps of the step , use a recursive apporach to make
        // sure each of the first step is started 

        // When the process starts the first step every nested steps is also started 
        const { steps = [] } = await Steps.get(queries);

        // From the ordered data , 
        var step = this.orderArray(steps, 'order')[0];

        // Create the step transactions record for each step 
        // and its sub steps
        await this.startStepBatch({ batch, step, body });

        // body = {
        //     ...body,
        //     step_id: step.id,
        //     process_id: process_id,
        //     start_time: DateUtils.getTime(),
        //     end_time: null
        // }

        // if (body.duration === 0) {

        //     body = {
        //         ...body,
        //         duration: 0,
        //         end_time: DateUtils.getTime(),
        //     }
        // }

        // If there is already an existing process transaction 
        // We have to move it to a new step according to the master
        if (process_transaction_id) {

            let params = {
                ...body,
                end_time: new Date(),

                // Other fields required for duration
            }

            // Get the reference to update
            const processTransactionReference = ProcessTransactions.getRecordReference(process_transaction_id);

            await batch.update(processTransactionReference, this.appendDefaultValues(params));

            return {
                process_transaction_id,

            }

        } else {

            // Get the reference to update
            const processTransactionReference = ProcessTransactions.getRecordReference();

            // Process transaction would record the start of the 
            // process and other fields releated to record , 
            // we can keep
            let processTransactionParams = {
                start_time: new Date(),
                end_time: null,
                ...body
            }

            // Note : Have to recheck how the data 
            // is flow 
            // Should we also consider 
            // 
            if (body.completed) {
                processTransactionParams.end_time = new Date();
            }

            // #Note Below looks buggy
            await batch.set(processTransactionReference, this.appendDefaultValues(processTransactionParams));

            return {
                process_transaction_id: processTransactionReference.id,
            }

        }
    }


    // pre_requisites = [{
    //     type: 'process/step',
    //     // Process/ Step
    //     type_id: '',
    //     // model: 'process/step',
    //     condition: 'start||end',
    //     hirarchy:''
    //     // Operators has to be considered 
    //     // 
    // }, {
    //     condition: 'end',
    //     step_id: '2',
    //     process_id: ''
    // }]


    // pre_requisites = [{
    //     type: 'process/step',
    //     // Process/ Step
    //     type_id: '',
    //     // model: 'process/step',
    //     condition: 'start||end',
    //     hirarchy:''
    //     // Operators has to be considered 
    //     // 
    // }, {
    //     condition: 'end',
    //     step_id: '2',
    //     process_id: ''
    // }]

    // var pre_requisites = "(type=process&type_id=1&condition=start)&&(type=process&type_id=1&condition=start)"


    /**
     * Get Config data for the timeline
     */
    getProcessTimeline({ identifier, id }) {

        var processQueries = [{
            field: 'identifier',
            value: identifier
        }]

        // Note , The process transaction cannot be linked with a record 
        var queries = [{ field: 'record_id', value: id }];

        return ProcessTransactions.get(queries).then(({ process_transactions }) => {

            console.log(process_transactions);

            // We load the process by the identifier in a sorted manner
            return this.get(processQueries, {
                orderBy: 'order'
            }).then((result) => {

                // For each of the process , we have to load the steps 
                return Promise.all(this.orderArray(result.process, 'order').map((process) => this.getSteps(process, process_transactions, id)));
            })
        })
    }


    /**
     * Get the process timeline for step
     * 
     */
    getProcessTimelineForStep = async (id, step_id, process_transaction_id) => {

        const process_trnsaction = await ProcessTransactions.getRecord(process_transaction_id);

        const step = await Steps.getDetail(step_id);

        // const finalData = await this.loadStepDetail(step, id, process_trnsaction);

        return await this.loadStepDetail(step, id, process_trnsaction);

        // return await this.prepareStepData(id, finalData, [process_trnsaction])

    }


    /**
     * Get all the steps of that process
     */
    getSteps(process, process_transactions = [], id) {

        var queries = [{
            field: 'process_id',
            value: process.id
        }, {
            field: 'active',
            value: true
        }]

        return Steps.get(queries, [], {
            orderBy: 'order'
        }).then((result) => {

            // We have to consider the transaction and step transaction for the process dashboard
            // #todo roles should be considered to decide
            // What needs to be shown
            return Promise.all(this.orderArray(result.steps, 'order').map(async (step) => {

                // We have to load the details of the step
                return await this.loadStepDetail(step, id, process_transactions);

                // return await this.prepareStepData(id, finalData, process_transactions);

            })).then((steps) => {

                return {
                    ...process,
                    process_transactions,
                    steps: steps
                }
            })
        })
    }


    /**
     * Load the all details of the step
     * 
     * Who it is assigned to 
     */
    async loadStepDetail(step, id, process_transactions) {

        // Load Sub Steps
        var queries = [{
            field: 'header_id',
            value: step.id
        }, {
            field: 'active',
            value: true
        }]

        // We Check if there are any sub steps under
        // the steps . 
        const { steps = [] } = await Steps.get(queries, { orderBy: 'order' })

        if (steps.length) {

            // For the step we have to prepare the step data which incldues the form /page 
            const final = await this.prepareStepData(id, step, process_transactions)

            // For all the sub steps , we have to load the details of it 
            const modifiedSteps = await Promise.all(steps.map((innerStep) => this.loadStepDetail(innerStep, id, process_transactions)))

            return {
                ...final,
                sub_steps: modifiedSteps
            }

        } else {

            // If there is no sub steps , we have to still load the steps information 
            const final = await this.prepareStepData(id, step, process_transactions)

            return {
                // ...step,
                ...final,
                sub_steps: []
            }
        }

        // if (step.role_id && step.role_id.length) {

        //     var queries = [{
        //         field: firebase.firestore.FieldPath.documentId(),
        //         operator: 'in',
        //         value: step.role_id
        //     }]

        //     const { roles } = await Roles.get(queries)

        //     return {
        //         ...step,
        //         roles
        //     }


        // } else {

        //     return {
        //         ...step,
        //     }
        // }
    }


    async getStepRole() {


    }



    /**
     * Prepare the step data to be shown in the timeline
     * We load the step transaction and use process transaction 
     * to enable the form 
     */
    prepareStepData(id, step, transactions) {

        let user = this.getUser();

        var queries = [{
            field: 'record_id',
            value: id
        }, {
            field: 'step_id',
            value: step.id
        }]

        // For the particular step id we have to check if there are any transactions active. 
        return StepTransactions.get(queries).then(({ step_transactions }) => {

            // Considering the process trnsaction current stage , we have to enable the form 
            // Also we consider the role 
            if (transactions.length && transactions[0].step_id === step.id) {

                // Decide who should see the form
                if (step.role_id && step.role_id.indexOf(user.role_id) !== -1) {

                    // if (user.role_id === step.role_id) {

                    step.formEnabled = true;

                }

                step.process_transaction = transactions[0]

                step.current_state = 'ongoing';

            } else {

                step.pending = true;

                step.current_state = 'pending';

            }

            step.step_transactions = [];

            // Below applicable cases
            // Its pending 
            // Its done
            // Its in process
            if (step_transactions.length) {

                if (step_transactions[0].completed) {

                    step.current_state = 'completed';

                } else {

                    step.current_state = 'ongoing';

                    // If there is a form or page linked to the step , 
                    // we have to activate the task form , 
                    // which would take care of the redirection to the page 
                    // or load the form to complete the step
                    if (step.form_id || step.page_id) {

                        step.formEnabled = true;
                    }



                }

                step.step_transactions = step_transactions;

            }

            return step

        })
    }


    get columns() {
        return [

            {
                caption: 'Name',
                render: (record) => {

                    return <Link className="booking-card" to={`/process/${record['id']}`}>
                        {record.name}
                    </Link>


                },
                key: 'created_at',
            },


            {
                caption: 'Description',
                field: 'description',
                key: 'description',
            },

            {
                caption: 'Identier',
                field: 'identier',
                key: 'identier',
            },

        ];
    }

    Card = (record) => {

        return (<Link className="booking-card" to={`process/${record['id']}`}>
            <div className="card">
                <h2 className="">

                    {record['amount']}

                </h2>
                <h3 className="head">

                    {record['name']}

                </h3>

                <h3 className="title">

                    {record['title']}

                </h3>

                <p className="address">
                    Booked on {record['Booking Date']}
                    <Tag color="magenta">{record['Consultant Name']}</Tag>
                </p>

            </div>
        </Link>)
    }

    DetailComponent = ({ model }) => <ProcessDetail model={model} />

    // AddComponent = ({ model }) => <ProcessAdd model={model} />

}

export default Process;

export { ProcessDashboard }