import * as i0 from '@angular/core';
import { InjectionToken, Inject, Optional, Injectable, NgModule } from '@angular/core';
import { SdUtilities } from '@sd-angular/core/utilities';
import { SdUtilities as SdUtilities$1 } from '@sd-angular/core/utilities/extensions';
import { lastValueFrom, from, throwError } from 'rxjs';
import { timeout, map, catchError, shareReplay, switchMap } from 'rxjs/operators';
import { v4 } from 'uuid';
import * as i1 from '@angular/common/http';
import { HttpResponse, provideHttpClient, HTTP_INTERCEPTORS, withInterceptorsFromDi } from '@angular/common/http';
import * as i2 from '@sd-angular/core/services/cache';

const SD_API_CONFIG = new InjectionToken('sd-api.configuration');
const TOKEN = SdUtilities.hash({
    origin: location.origin,
    key: 'ed4a2ffc-af43-41fb-9ac9-970f43d99571',
});

class SdApiService {
    httpClient;
    configurations;
    cacheService;
    #defaultTimeout = 60000; // 60s
    #dedupCacheDuration = 1000; // 1s (Deduplication cache duration)
    // Thay đổi cấu trúc Cache: Lưu Observable thay vì Subject phức tạp
    // Key: hash string -> Value: { stream$: Observable, expiry: number }
    #inFlightRequests = new Map();
    constructor(httpClient, configurations, cacheService) {
        this.httpClient = httpClient;
        this.configurations = configurations;
        this.cacheService = cacheService;
        // Optional: Cơ chế dọn dẹp cache định kỳ (mỗi 1 phút dọn dẹp các key hết hạn)
        setInterval(() => this.#cleanupCache(), 60000);
    }
    get http() {
        return this.httpClient;
    }
    // --- PUBLIC METHODS ---
    get = (url, option) => {
        return this.#executeWithLayeredCache(url, 'GET', undefined, option);
    };
    post = (url, body, option) => {
        return this.#executeWithLayeredCache(url, 'POST', body, option);
    };
    put = (url, body, option) => {
        return this.#executeWithLayeredCache(url, 'PUT', body, option);
    };
    delete = (url, option) => {
        return this.#executeWithLayeredCache(url, 'DELETE', undefined, option);
    };
    // Upload file logic giữ nguyên nhưng refactor nhẹ
    upload = async (url, option) => {
        const file = await SdUtilities$1.upload(option);
        if (!Array.isArray(file) && file) {
            return this.uploadFile(url, file);
        }
    };
    uploadFile = async (url, file) => {
        if (!file)
            return null;
        if (file.name.lastIndexOf('.') === -1)
            throw new Error('Invalid file extension');
        const formData = new FormData();
        formData.append('file', file, file.name);
        // Upload thường không cần deduplication cache, set autoCache: false
        return await this.post(url, formData, { autoCache: false });
    };
    // --- PRIVATE CORE LOGIC ---
    /**
     * Hàm trung gian xử lý Layer Cache (SdCacheService) trước khi gọi API thực tế
     */
    #executeWithLayeredCache = async (url, method, body, option) => {
        // Layer 1: Persistent Cache (SdCacheService)
        if (option?.cacheOption) {
            const key = this.#generateKey(url, method, body, option);
            const { get, set, has } = this.cacheService.create(key, option.cacheOption);
            if (has()) {
                return get();
            }
            const result = await this.#request(url, method, body, option);
            set(result);
            return result;
        }
        // Không có cache dài hạn thì gọi trực tiếp logic deduplication
        return this.#request(url, method, body, option);
    };
    /**
     * Core Request: Xử lý Deduplication, Timeout, Mapping Response
     */
    #request = (url, method, body, option) => {
        const key = this.#generateKey(url, method, body, option);
        // Layer 2: Deduplication Cache (In-Flight Request / Short-term cache)
        const now = Date.now();
        const cachedItem = this.#inFlightRequests.get(key);
        // Nếu đã có request đang chạy hoặc mới chạy xong trong vòng 1s -> Trả về stream đó luôn
        if (cachedItem && cachedItem.expiry > now) {
            return lastValueFrom(cachedItem.stream$);
        }
        // Setup request mới
        const handler = this.#getHandler(url);
        const apiTimeout = option?.timeout ?? handler?.timeout ?? this.#defaultTimeout;
        // Tạo Observable call API
        const request$ = this.httpClient
            .request(method, url, {
            body,
            headers: option?.headers,
            params: option?.params,
            observe: 'response', // Lấy full response để check status
            responseType: option?.responseType,
        })
            .pipe(timeout(apiTimeout), map(res => {
            // Normalize Response Logic
            const bodyRes = res.body;
            // Logic check response cũ của bạn
            if (bodyRes && typeof bodyRes === 'object' && 'ok' in bodyRes && !bodyRes.ok) {
                throw bodyRes; // Giả sử structure trả về { ok: false, ... } là lỗi
            }
            if (handler?.mapResponse) {
                return handler.mapResponse(bodyRes);
            }
            return bodyRes;
        }), catchError(err => {
            // Xóa cache ngay lập tức nếu lỗi để user có thể retry
            this.#inFlightRequests.delete(key);
            throw err;
        }), 
        // QUAN TRỌNG: shareReplay(1) giúp share kết quả cho các subscriber đến sau (trong 1s)
        shareReplay(1));
        // Lưu vào Map
        this.#inFlightRequests.set(key, {
            stream$: request$,
            expiry: now + this.#dedupCacheDuration,
        });
        // Chuyển đổi sang Promise cho đúng return type của bạn
        return lastValueFrom(request$);
    };
    // --- HELPERS ---
    #getHandler = (url) => {
        const handlers = this.configurations?.flatMap(b => b?.handlers || []) || [];
        return handlers.find(e => e.hosts.some(host => url.startsWith(host)));
    };
    #generateKey = (url, method, body, option) => {
        // FormData không hash được nội dung file, luôn generate key mới
        if (body instanceof FormData || option?.autoCache === false) {
            return v4();
        }
        return SdUtilities$1.hash({
            url,
            method,
            params: option?.params,
            headers: option?.headers,
            body,
        });
    };
    // Dọn dẹp memory leak
    #cleanupCache() {
        const now = Date.now();
        this.#inFlightRequests.forEach((value, key) => {
            if (value.expiry < now) {
                this.#inFlightRequests.delete(key);
            }
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiService, deps: [{ token: i1.HttpClient }, { token: SD_API_CONFIG, optional: true }, { token: i2.SdCacheService }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [SD_API_CONFIG]
                }, {
                    type: Optional
                }] }, { type: i2.SdCacheService }] });

/* eslint-disable @typescript-eslint/no-explicit-any */
class SdHttpInterceptor {
    configurations;
    constructor(configurations) {
        this.configurations = configurations;
    }
    intercept(request, next) {
        const url = request.url;
        if (!url) {
            throw new Error(`Invalid URL`);
        }
        const handlers = this.configurations?.reduce((a, b) => [...a, ...(b?.handlers || [])], []) || [];
        const handler = handlers?.find(e => e.hosts.some(host => url.startsWith(host)));
        const intercept = handler?.intercept;
        if (intercept) {
            request = request.clone(intercept(request));
        }
        if (request.body instanceof FormData) {
            request = request.clone({
                headers: request.headers.delete('Content-Type'),
            });
        }
        const beforeRemoteHandler = handler?.beforeRemote;
        const afterRemoteHandler = handler?.afterRemote;
        const beforeRemote = beforeRemoteHandler?.(request);
        if (beforeRemote instanceof Promise) {
            return from(beforeRemote).pipe(switchMap(() => next.handle(request)), map((event) => {
                if (event instanceof HttpResponse) {
                    afterRemoteHandler?.(event);
                }
                return event;
            }), catchError((error) => {
                afterRemoteHandler?.(error);
                return throwError(() => error);
            }));
        }
        return next.handle(request).pipe(map((event) => {
            if (event instanceof HttpResponse) {
                afterRemoteHandler?.(event);
            }
            return event;
        }), catchError((error) => {
            afterRemoteHandler?.(error);
            return throwError(() => error);
        }));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdHttpInterceptor, deps: [{ token: SD_API_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdHttpInterceptor });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdHttpInterceptor, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [SD_API_CONFIG]
                }, {
                    type: Optional
                }] }] });

class SdApiModule {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: SdApiModule });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiModule, providers: [provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: SdHttpInterceptor, multi: true }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdApiModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [],
                    exports: [],
                    providers: [provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: SdHttpInterceptor, multi: true }],
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { SD_API_CONFIG, SdApiModule, SdApiService, SdHttpInterceptor, TOKEN };
//# sourceMappingURL=sd-angular-core-services-api.mjs.map
