import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output, TemplateRef } from '@angular/core';
import { DonutChartConfig } from '@red/domain';
import { IqserHelpModeModule } from '@iqser/common-ui';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { AsyncPipe, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
import { MatSelectModule } from '@angular/material/select';
import { FilterService, INestedFilter } from '@iqser/common-ui/lib/filtering';
import { get, shareLast } from '@iqser/common-ui/lib/utils';
import { StatusBarComponent } from '@iqser/common-ui/lib/shared';

@Component({
    selector: 'redaction-donut-chart',
    templateUrl: './donut-chart.component.html',
    styleUrls: ['./donut-chart.component.scss'],
    standalone: true,
    imports: [NgForOf, NgIf, MatSelectModule, IqserHelpModeModule, StatusBarComponent, AsyncPipe, NgTemplateOutlet],
})
export class DonutChartComponent implements OnChanges, OnInit {
    @Input() subtitles: string[] = [];
    @Input() config: DonutChartConfig[] = [];
    @Input() radius = 85;
    @Input() strokeWidth = 20;
    @Input() direction: 'row' | 'column' = 'column';
    @Input() totalType: 'sum' | 'count' | 'simpleLabel' = 'sum';
    @Input() counterText: string;
    @Input() filterKey;
    @Input() helpModeKey;
    @Input() valueFormatter?: (value: number) => string;
    @Input() subtitleTemplate?: TemplateRef<any>;

    @Output() readonly subtitleChanged = new EventEmitter<number>();

    chartData: any[] = [];
    cx = 0;
    cy = 0;
    size = 0;
    filters$: Observable<INestedFilter[]>;

    get circumference(): number {
        return 2 * Math.PI * this.radius;
    }

    get dataTotal(): number {
        return this.config.map(v => v.value).reduce((acc, val) => acc + val, 0);
    }

    get displayedDataTotal() {
        return this.totalType === 'sum' ? this.dataTotal : this.config.length;
    }

    constructor(@Optional() readonly filterService: FilterService) {
        // TODO: move this component to a separate module, split into smaller components, improve filters
    }

    ngOnInit() {
        const filterModels$ = this.filterService?.getFilterModels$(this.filterKey).pipe(
            map(filters => filters ?? []),
            shareLast(),
        );

        this.filters$ = filterModels$ ?? of([]);
    }

    ngOnChanges(): void {
        this.calculateChartData();
        this.cx = this.radius + this.strokeWidth / 2;
        this.cy = this.radius + this.strokeWidth / 2;
        this.size = this.strokeWidth + this.radius * 2;
    }

    filterChecked$(key: string): Observable<boolean> {
        return this.filters$.pipe(
            get(filter => filter.id === key),
            map(filter => !!filter?.checked),
        );
    }

    getFormattedValue(value: number): string {
        return this.valueFormatter ? this.valueFormatter(value) : value.toString();
    }

    calculateChartData() {
        let angleOffset = -90;
        this.chartData = this.config.map(dataVal => {
            if (dataVal.value === 0) {
                return null;
            }

            const res = { degrees: angleOffset };
            angleOffset = this.dataPercentage(dataVal.value) * 360 + angleOffset;
            return res;
        });
    }

    calculateStrokeDashOffset(dataVal: number): number {
        const strokeDiff = this.dataPercentage(dataVal) * this.circumference;
        return this.circumference - strokeDiff;
    }

    dataPercentage(dataVal: number): number {
        return dataVal / this.dataTotal;
    }

    returnCircleTransformValue(index: number) {
        return `rotate(${this.chartData[index].degrees}, ${this.cx}, ${this.cy})`;
    }

    getLabel({ label, value }: DonutChartConfig): string {
        return this.totalType === 'simpleLabel'
            ? `${label}`
            : this.totalType === 'sum'
            ? `${this.getFormattedValue(value)} ${label}`
            : `${label} (${this.getFormattedValue(value)} ${this.counterText})`;
    }

    selectValue(key: string): void {
        this.filterService?.toggleFilter(this.filterKey, key);
    }
}
