import { FieldForm,CalcFieldForm,FieldType, CalcFormat} from "../types/FieldLayout";

/**
 * 集計操作子
 */
export enum Operator {
    SUM = "SUM",
    AVG = "AVG",
    MAX = "MAX",
    MIN = "MIN",
    COUNT = "COUNT",
    FIRST = "FIRST",
    LAST = "LAST"
}

/**
 * 集計関数
 */

export class Aggregator {
    private data: string[];
    private field: FieldForm;

    constructor(data: string[], field: FieldForm) {
        this.data = data;
        this.field = field;
    }

    private toNumberArray(): number[] {
        const numberArray = this.data.map(item => {
            const num = Number(item);
            if (isNaN(num)) {
                throw new Error(`フィールド「${this.field.code} 」は数値に変換できないため、計算を実行できません。`);
            }
            return num;
        });

        return numberArray;
    }

    private sum(): number | null {
        if (this.data.length === 0) return null;
        const numberArray = this.toNumberArray();
        return numberArray.reduce((acc, val) => acc + val, 0);
    }

    private avg(): number | null {
        if (this.data.length === 0) return null;
        const numberArray = this.toNumberArray();
        const total = numberArray.reduce((acc, val) => acc + val, 0);
        return total / numberArray.length;
    }

    private max(): number | string | null {
        if (this.data.length === 0) return null;
        if (this.field.type === FieldType.NUMBER ) {
            const numberArray = this.toNumberArray();
            return Math.max(...numberArray);
        }
        if(this.field.type===FieldType.CALC){
            const calcField = this.field as CalcFieldForm;
            if(calcField.format===CalcFormat.NUMBER || calcField.format===CalcFormat.NUMBER_DIGIT){
                const numberArray = this.toNumberArray();
                return Math.max(...numberArray);
            }
        }
        return this.data.reduce((max, item) => (item > max ? item : max), this.data[0]);
    }

    private min(): number | string | null {
        if (this.data.length === 0) return null;
        if (this.field.type === FieldType.NUMBER ) {
            const numberArray = this.toNumberArray();
            return Math.min(...numberArray);
        }
        if(this.field.type===FieldType.CALC){
            const calcField = this.field as CalcFieldForm;
            if(calcField.format===CalcFormat.NUMBER || calcField.format===CalcFormat.NUMBER_DIGIT){
                const numberArray = this.toNumberArray();
                return Math.min(...numberArray);
            }
        }
        if(this.data===null || this.data.length===0){
            return null;
        }
        return this.data.reduce((min, item) => (item < min ? item : min), this.data[0]);
    }

    private count(): number {
        return this.data.length;
    }

    private first(): string | null {
        return this.data.length ? this.data[0] : null;
    }

    private last(): string | null {
        return this.data.length ? this.data[this.data.length - 1] : null;
    }

    public calculate(operator: Operator): number | string | null {
            switch (operator) {
                case Operator.SUM:
                        return this.sum();
                case Operator.AVG:
                        return this.avg();
                case Operator.MAX:
                    return this.max();
                case Operator.MIN:
                    return this.min();
                case Operator.COUNT:
                    return this.count();
                case Operator.FIRST:
                    return this.first();
                case Operator.LAST:
                    return this.last();
                default:
                    return null;
            }
    }
}